Search code examples
javakotlinabstract-classcovariance

Type inference failed for a Java class definition. How can I solve this?


In Kotlin I have found that, in contrast to Java, the following can be done using covariance:

ArrayList<Dog> listOfDogs = listOf(dog1, dog2)
ArrayList<Animal> animalList = listOfDogs // No compiler error

In my situation I have an interface Message:

public interface Message {
     RawMessage toRawMessage();
}

For example I have an Int32 Message:

public interface Int32 extends Message {
    // abstract methods
} 

Now I have a method with a parameter type of type Class<Message>:

fun doSomethingWithType(type: Class<Message>) {
    // Implementation
}

The problem is that I am trying to pass a Java class extending from Message like:

doSomethingWithType(Int32::class.java)

But this gives me the following compiler error:

Expected type mismatch: 
required: Class<Message> 
found:    Class<Int32>

Why is this not working and how should I solve it?


Solution

  • The following is valid Kotlin code (assuming Dog is a subclass of Animal):

    val listOfDogs: List<Dog> = listOf(dog1, dog2)
    val listOfAnimals: List<Animal> = listOfDogs
    

    The reason behind this is that List is an Kotlin interface defined as:

    public interface List<out E> : Collection<E>
    

    Unfortunately you can't do the same in pure Java, but you can do this:

    List<Dog> listOfDogs = new ArrayList<>();
    List<? extends Animal> listOfAnimals = listOfDogs;
    

    Now, doSomethingWithType expects a Class<Message> while you're providing Class<Int32>, and Class is not covariant in T. In order to make that code compile you can do the following:

    fun doSomethingWithType(type: Class<out Message>) { ... }
    doSomethingWithType(Int32::class.java)
    

    This will work as long as you'll only pull out T from Class<T>, i.e. Class will only produce type T.