An invariant generic class does not relate different type arguments through subtyping: neither MutableList<String> nor MutableList<Any> is a subtype of the other. This prevents unsafe substitutions when a class can both produce and consume values. Java classes are invariant by default, while Kotlin separately supports covariant and contravariant declarations. section 11.3.2 section 11.3.3
A collection-like class remains invariant
```kotlin open class Animal { fun feed() { /* ... */ } } class Herd<T : Animal> { val size: Int get() = /* ... */ operator fun get(i: Int): T { /* ... */ } } fun feedAll(animals: Herd<Animal>) { /* ... */ } class Cat : Animal() { fun cleanLitter() { /* ... */ } } fun takeCareOfCats(cats: Herd<Cat>) { cats[0].cleanLitter() // feedAll(cats) // type mismatch } ``` Because `Herd<T>` has no variance modifier, `Herd<Cat>` is not a subtype of `Herd<Animal>`. An explicit cast may force the call, but it is described as verbose, error-prone, and generally the wrong response to this mismatch.
Invariance can be relaxed at a particular use site when an invariant class is used in only one direction. An out-projected MutableList<out T> can be read as a producer, while MutableList<in T> can be used as a consumer. The restriction is visible in the API: an out-projected list cannot accept an element through add. section 11.3.5
Copying between invariant lists
```kotlin fun <T> copyData( source: MutableList<out T>, destination: MutableList<T> ) { for (item in source) destination.add(item) } val ints = mutableListOf(1, 2, 3) val anyItems = mutableListOf<Any>() copyData(ints, anyItems) ``` The source is restricted to reading and the destination to writing, so values from a more specific list can be copied into a list whose element type is a supertype.
