Mutable/Immutable collection covariance



It’s a bit unclear to me how does immutable collection covariance work.
Quote from documentation (Collections):
Note that the read only types are covariant. That means, you can take a List<Rectangle> and assign it to List<Shape> assuming Rectangle inherits from Shape. This wouldn't be allowed with the mutable collection types because it would allow for failures at runtime.

Demo code:

open class Shape
class Rectangle : Shape()

fun main(args: Array<String>) {
    val rectangles: List<Rectangle> = listOf(Rectangle())
    val workedFine: List<Shape> = rectangles // perfectly valid

    val mutableRectangles: MutableList<Rectangle> = mutableListOf(Rectangle())
    val doesntWork: MutableList<Shape> = mutableRectangles // type mismatch

In Kotlin stdlib sources MutableList defined in the same way as read-only List.

So how does this different behaviour implemented from technical perspective ? Is this a very special treatment of immutable\read-only predefined data types?


Here are the sources of kotlin collection interfaces:

Notice that the readonly list is declared as
public interface List<out E>
and the mutable list as
public interface MutableList<E>.

The difference is that type parameter of the former is prefixed with the out modifier, which indicates the variance of that type parameter. More about variance in the documentation.


My bad, you are right, @ilya.gorbunov, thank you!

I missed this out E variance part of definition :frowning:

public interface List<out E> : Collection<E> { .....}
public interface MutableList<E> : List<E>, MutableCollection<E> { .... }