Type Parameter inference results in NullPointerException?


#1

The following code tells everything:

class A<E>(val a: E) {
    fun <T> get(s: T?) = a as T

    fun get(): E = get(null) //Compiles OK, but throw NullPointerException when running.

    fun get2(): E = get<E>(null) //Compiles OK and no exceptions.
}

fun main(args: Array<String>) {
    val a = A(1)
    println(a.get2())
    println(a.get())
}

running result is

1
Exception in thread "main" java.lang.NullPointerException
	at A.get(Bug.kt:13)
	at A.main(Bug.kt:21)

#2

Using Kotlin Bytecode to decompile the code, we get:

public final Object get() {
      this.get((Object)null);
      throw null;// Why?
 }

public final Object get2() {
      return this.get((Object)null);
}

Which shows that NullPointerException is determined at compilation and no warning about this.Only runtime NullPointerException is thrown.


#3

T is different for get() and get2(): Nothing and Int respectively. So that explains why the behavior is different. You can see the inferred type in IntelliJ IDEA if you use this code:

val getBody = a.get(null) // Inferred type is Nothing
val get2Body = a.get<Int>(null) // Inferred type is Int

The fact that the resulting exception of get() is a NullPointerException, seems a bug to me. If I add a function with Nothing as the explicit type to A, I get a ClassCastException when I call it:

fun getNothing(s: Nothing?): Nothing = a as Nothing

Results:

val getBody = a.get(null) // Results in NullPointerException
val getBodyNothing = a.get<Nothing>(null) // Results in NullPointerException
val explicitNothing = a.getNothing(null) // Results in ClassCastException: java.lang.Integer cannot be cast to java.lang.Void

#5

No, it doesn’t. When calling get(T) without a type parameter, the compiler infers the common type of the type of a and the null that you pass to get(T): Nothing.


#6

yeah. val doesn’t help type inference. so the type of a.get(null) is Nothing
If you change the code to val getBody:Int=a.get(null), then the type is Int


#7

When giving the arguments, the compiler doesn’t infer the type from the returned type?


#8

As far as I know it uses both the argument type and the return type, and selects the most specific common type of both types.