Mutable/Immutable collection covariance

Hello!

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?

1 Like

Here are the sources of kotlin collection interfaces: kotlin/Collections.kt at 1.1.0 · JetBrains/kotlin · GitHub

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.

1 Like

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> { .... }