Skip to content
Advertisement

Why is one able to assign a list of child type to a list of its superclass type, in Kotlin?

I have read topics on Generics and Wildcards in Kotlin and also their differences compared with Java, I have tried to search online, but I couldn’t find the answer to this question nor make sure if anyone has asked it.

I’ve got the class Note and the class FavouriteNote, derived from the class Note. I’ve also got an array list with type parameter Note and an array list with type parameter FavouriteNote. I’m trying to assign List<FavouriteNote> to List<Note>, which of course won’t work in Java.

JavaScript

In Kotlin, though, I am free to assign List<FavouriteNote> to List<Note>:

JavaScript

Why is that or what can I read to learn more about how this works in Kotlin and what is happening under the hood?

Advertisement

Answer

For the following examples, let’s introduce one more class to use:

JavaScript

The biggest difference between Kotlin and Java generics is that Kotlin introduces declaration site variance for classes and interfaces. Java only has use site variance for classes and interfaces.

The variance of variable in Java can only be declared at the use site. If you don’t specifically declare a List to be covariant, it is invariant:

JavaScript

This is the type system preventing you from making a mistake. Suppose the above code didn’t throw an error. Then this could happen:

JavaScript

Incidentally, Kotlin’s MutableList does not make use of declaration site variance, so you would have the exact same restriction:

JavaScript

However, you can use use-site variance to make the cast possible. We can declare the list to be covariant at the use site:

JavaScript
JavaScript

You are protected from the ClassCastException situation above because the compiler prevents you from adding items to a covariant list.


Now getting to declaration site variance. Kotlin declares the read-only List interface’s type to be <out T> right in the definition of the interface. This means all Lists are automatically assumed to be covariant, even if you don’t bother to declare it to be covariant at the use site.

If you declare variance at a class or interface’s declaration site, the compiler will restrict you from making functions or properties that violate that variance. The read-only List interface doesn’t have any add functions, so it is fine for it to be a T producer and not consumer.

User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement