Home > Enterprise >  Java superclass and subclass generics in Kotlin
Java superclass and subclass generics in Kotlin

Time:07-10

I'm looking for documentation or specifications about Java generics in Kotlin.
I'm curious about the behavior that is different between the superclass and the subclass.

For example, here is the following Java code.

// A.java
public class A<T> {
    public T value;
    public A(T value) {
        this.value = value;
    }
}

// B.java
public class B<T> extends A<T> {
    public B(T value) {
        super(value);
    }
}

Use the above Java code from Kotlin.

fun main() {
    val a = A<String>("a")
    val b = B<String>("b")

    val aValue = getValue(a)
    val bValue = getValue(b)

    // lint error: Condition 'aValue == null' is always 'false'
    // aValue is non-null type
    println(aValue == null)

    // No lint error
    // bValue is platform type(String!)
    println(bValue == null)
}

fun <T> getValue(a: A<T>): T {
    return a.value
}

In this case, in the case of the superclass, the result of the function is marked as non-null.
On the other hand, in the case of the subclass, the result of the function is marked as platform type.

Does anyone know about this specification?
It would be helpful if you could tell me the official documents etc.

CodePudding user response:

The compiler can figure out that aValue == null is always false, because the type of aValue is A<String>, and after type inference, the type parameter T of getValue is inferred to be the non-nullable String. Note that T cannot be String? here, because A<String> is not a subtype of A<String?>, in the same way that List<Dog> is not a List<Animal>. Therefore, String is what getValue returns for aValue.

On the other hand, the type of bValue is B<String>. What does type inference say about the type parameter T? Well, T could be either String or String?. B<T> could inherit A<T> with T being non-nullable, but B<T> could also inherit A<T?>, couldn't it? There is no annotation in the Java code that says anything about this. Therefore, the Kotlin compiler chose to be cautious and infers T to be String?, assuming that B<T> inherits from A<T?>.

If you actually meant that B<T> inherits from A<T>, mark that in the Java code with one of those annotations that Kotlin recognises:

class B<T> extends A<@NotNull T> {

If you don't have control over the Java code, you can also specify the type parameter explicitly:

val bValue = getValue<String>(b)

Since platform types are platform-specific, they are not in the Kotlin/Core spec. This specific behaviour will probably be on the Kotlin/JVM spec, which isn't released yet :(

  • Related