Using Generic as non-nullable property

It is possible to define a property, that is nullable regardless of the nullability of the generic:

interface Dut<T> {
   val field: T?
}

I would like to have a field the other way round, but I can not define the generic self as not nullable, as field may or may not be nullable:

interface Dut<T> {
   val field: T // may be nullable or not
   val listWithoutNulls: List<T!>  // the exclamation mark is actually not working, but how may I define this?
}
1 Like

You can define a base generic type as non-nullable:

interface Dut<T : Any> {
	val field1: T // will always be non-null
	val field2: T? // can be null
	val field3: List<T> // list of non-nulls
	val field4: List<T?> // list of potential nulls
}
1 Like

I can’t define it like that, as mentioned in the example above.

At the call site the “user” determines if the property field may be null or not, but the property listWithoutNulls is always a list without nulls:

val dut1: Dut<String> // field is not nullable, the list contains strings
val dut2: Dut<String?> // field is nullable, the list contains strings

I can not achieve this with example.

You are a little wrong here. The line val dut2: Dut<String?> won’t even compile if you put the type constraint I showed :wink: <T : Any> means that a type can’t even be declared as nullable.

1 Like

I think @jano wants to allow the caller to specify nullability for some member types but force others.

Currently you can force a return type to be a nullable version given a T, but he wants to force a non-nullable given any T.

1 Like

Yes, I refer to my example above, where exactly this is (should be) possible.

Here’s a runnable example of the question:

interface Foo<T> {
    val couldBeNullableOrNot: T
    val nullableAlways: T? // Totally fine even if T is already nullable.
}

interface Bar<T> {
    val couldBeNullableOrNot: T
    val neverNull: T!! // ERROR How does one specify non-nullable
}

interface Bizz<T> {
    val couldBeNullableOrNot: T
    fun <B> neverNull(): B where 
    	B : T, 
    	B : Any // ERROR Can't use multiple bounds when bounded by another type param
}

fun main() { println("noop") }
1 Like

Then how about this?

interface Dut<T : E?, E : Any> {
	val field: T
	val listWithoutNulls: List<E>
}

E must be non-nullable, but T can be either nullable or not depending on call-site. You can either use type inference or you must specify both types at once, eg:

Dut<Int?, Int>
Dut<Int, Int>
1 Like

Thanks a lot, that seems to do the trick. It may be nullable and both types have to be exactly the same.

I don’t like it very much, as I always have to specify both types, but it is a workaround. If anyone has a solution with a single generic I would be fully satisfied :slight_smile:

1 Like

Maybe it would be possible if they implement default generic parameters: Default Types for Generics. For now it’s the best I could think of :slight_smile:

A similar feature already exists in TypeScript: Generic Parameter Defaults in TypeScript — Marius Schulz.

funny thing here is that the notation for the type already exists, but it cannot be specified explicitly, it exists just to present type information to the reader:
image

2 Likes

Maybe it’s time to add it for real then :wink:

1 Like

and this one too? :slight_smile:
image

disclaimer: I’m not from Kotlin team, I’m just a user of Kotlin, like you, but from JetBrains

1 Like

and maybe this syntax should also be added?


(sorry for off-topic)

1 Like

I created a feature request.

It’s marked as duplicate of another request (that is not exactly the same, but the feature is included in the discussion - so good enough), that seems to be merged.

2 Likes

It seems like the problem now can be solved in much more straightforward way than before using the new Definitely non-nullable types feature:

interface Dut<T> {
	val field: T
	val listWithoutNulls: List<T & Any>
}

I don’t think is good option the definitely non-nullable types:

https://youtrack.jetbrains.com/issue/KT-52173/Unconsistency-of-generic-and-non-generic-types-using-Definitely-

Since Kotlin 1.7 it is also possible to use Underscore operator for type arguments feature to simplify my previous solution:

data class Dut<T : E?, E : Any>(
	val field: T,
	val listWithoutNulls: List<E>,
)

val dut = Dut<Int?, _>(null, listOf(1, 2, 3)))

Although for this case probably Definitely non-nullable types still seems better to me.

1 Like