Keep nullability of function parameter type on return type?

You can create a function that maintains the nullability of the parameter, on the return type if the parameter and the return type are the same.

See this example:

fun <T : String?> foo(bar: T): T = TODO()

For this function the return type relates to the parameter type like this:

Parameter type Return type
String String
String? String?

Now my question is if it is possible to do the same thing but with the parameter type and the return type being different.

Something like this:

Parameter type Return type
String Int
String? Int?

Thanks in advance.

You would have to use 2 parameters

fun <T: String?, R: Int?> foo(bar: T): R = TODO()

I don’t see any other way of doing it without a huge mess of code

That’s not type safe. There is nothing preventing you from writing val i: Int = foo(null) with that signature.

Unfortunately, the only way to achieve the desired result is by overloading the function with 2 versions:

fun foo(s: String) : Int
fun foo(s: String?) : Int?
1 Like

I recently discussed an idea that could solve this on the kotlin slack.

data class Test<?null>(
    val foo: Foo<?null>,   // nullable depending on class parameter
    val bar: Bar,      // never null
    val baz: Baz:?     // allways nullable
)
val test1 = Test<?>(getFooOrNull(), Bar(), null)
val test2 = Test<!>(Foo(), Bar(), null)
test1.foo?.doSomething()
test2.foo.doSomething()

The idea is to have a generic parameter that just contains nullability data. That way your code could look something like

fun foo<?null, T: String>(bar: T<?null>): T<?null> = TODO()

The current syntax is terrible so if anyone has a better idea it would be great :wink:

1 Like

You can introduce an overload for the non-null type. It will choose that one as it is more specific. The only issue there is that you must specify an @JvmName annotation for either (the non-null version preferably) as the JVM doesn’t see the difference in signature - it is valid Kotlin, for Kotlin the signature is different). Alternatively, you may be able to use contracts to handle this instead.

1 Like

It would be interesting to see if you can come up with a contract to solve this. I had the same idea some time ago, but couldn’t figure it out (or simply it wasn’t / isn’t possible), and I’ve ended up with the overload approach.