Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a generic typed property nullable?

Tags:

kotlin

I am trying to create a parameterized class with a lateinit non-nullable property of the generic type:

class Test<T> {     private lateinit var t : T     private lateinit var s : String  } 

The latter is allowed, but the former is not. The compiler returns the following error:

Error:(7, 11) ''lateinit'' modifier is not allowed on nullable properties

Since I didn't declare T?, I am confused as to why this is the case.

like image 760
nhaarman Avatar asked Oct 08 '15 16:10

nhaarman


People also ask

Can a generic type be null?

The definition of a generic type should never be nullable. The nullability should be defined by the one using this generic. With the current Kotlin version, you can have a "String?" implementation and a "String" implementation.

Why we use nullable types in C#?

You typically use a nullable value type when you need to represent the undefined value of an underlying value type. For example, a Boolean, or bool , variable can only be either true or false . However, in some applications a variable value can be undefined or missing.

What are nullable data types?

The Nullable type allows you to assign a null value to a variable. Nullable types introduced in C#2.0 can only work with Value Type, not with Reference Type. The nullable types for Reference Type is introduced later in C# 8.0 in 2019 so that we can explicitly define if a reference type can or can not hold a null value.

What is the difference between nullable and non nullable properties in Android?

Understand non-nullable and nullable variablesA type is only nullable if you explicitly let it hold null . As the error message says, the String data type is a non-nullable type, so you can't reassign the variable to null . To declare nullable variables in Kotlin, you need to add a ? operator to the end of the type.


2 Answers

The default upper bound (if none specified) is Any? (Source)

In other words, when you use T, Kotlin assumes that this might be any type, be it primitive, object or a nullable reference.

To fix this add an upper type:

class Test<T: Any> { ... } 
like image 101
voddan Avatar answered Sep 30 '22 04:09

voddan


Nullable Type Parameter

Any? is the supertype of all types in Kotlin. So, when you don't specify any upper bound for the type parameter T, the default bound is Any?.

For example:

class Test<T> { }

is the same as

class Test<T : Any?> { }

This results in the T being nullable in the following example:

class Test<T> {     private var t : T          // T can have a nullable type } 

This means that the generic type above can be instantiated with nullable as well as non-null type arguments:

val test: Test<Int> = Test()   // OK val test: Test<Int?> = Test()  // OK 

Non-null Type Parameter

Any is the supertype of all non-null types in Kotlin. So, to make a generic class accept only non-null type arguments, you need to explicitly specify Any as an upper bound of T, that is T : Any.

This results in the T being non-null in the following example:

class Test<T : Any> {     private var t: T           // T is non-null     private var t2: T?         // T can be used as nullable } 

The generic type with T : Any can be instantiated only with non-null type arguments and prevents the instantiation with nullable type arguments:

val test: Test<Int> = Test()   // OK val test: Test<Int?> = Test()  // Error 

lateinit var

The lateinit var must always be non-null because it is used in the cases where you want a variable to be non-null but don't want to initialize its value at the time of object creation.

So, to create the lateinit variable that has the same type as the type parameter T, the type parameter needs to be non-null too.

To achieve that, specify the upper bound T : Any explicitly:

class Test<T : Any> {     private lateinit var t: T } 

It's worth noting that you can use a more specific type, if you have one depending on your business logic. For example, instead of T : Any, you could have T : SomeProduct, if that is what you want the upper bound to be. It just needs to be non-null.

This will ensure that the user of your class won't be able to instantiate with nullable type arguments and your assumption of the lateinit var always being non-null will hold true.


That's it! Hope that helps.

like image 26
Yogesh Umesh Vaity Avatar answered Sep 30 '22 02:09

Yogesh Umesh Vaity