Variance

Variance: in, out and Type Projections

Is a list of Ebooks a list of Products? For a read-only List, yes: you can only take products out. For a MutableList, no: code holding it as MutableList<Product> could add a paper book. Kotlin marks this on the type parameter. out T only produces T and is covariant; in T only consumes T and is contravariant; no modifier means invariant. Hence List<out E>, Comparable<in T>, MutableList<E> and Flow<out T> (Coroutines and Flow).

Covariance with out and contravariance with in
Covariance with out and contravariance with in

The compiler enforces both rules:

Variance errors caught at compile timeKotlin
open class Product
class Ebook : Product()
interface Source<out T> { fun push(item: T) }
val products: MutableList<Product> = mutableListOf<Ebook>()
Output
varerr.kt:3:42: error: type parameter 'T' is declared as 'out' but occurs in 'in' position in
  type 'T (of interface Source<out T>)'.
varerr.kt:4:36: error: initializer type mismatch: expected 'MutableList<Product>', actual
  'MutableList<Ebook>'.

When a class is invariant but a function only reads or only writes it, put the modifier at the use site. This type projection is Java's ? extends and ? super. A star projection, List<*>, reads elements as Any? and accepts no additions.

Covariance, contravariance, projections and a star projectionKotlin
open class Product(val title: String) { override fun toString() = title }
class Ebook(title: String) : Product(title)
fun interface Sink<in T> { fun accept(item: T) }
fun copyInto(from: MutableList<out Product>, to: MutableList<in Product>) { to += from }
fun main() {
  val products: List<Product> = listOf(Ebook("Patterns of the Deep Web"))  // out
  val log: Sink<Ebook> = Sink<Any> { println("Logged $it") }                // in
  log.accept(Ebook("Gardens in Glass"))
  val shelf: MutableList<Any> = mutableListOf("header")
  copyInto(mutableListOf(Ebook("Salt and Saffron")), shelf)
  val unknown: List<*> = shelf
  println("${products.size} product; shelf has ${unknown.size}: $unknown")
}
Output
Logged Gardens in Glass
1 product; shelf has 2: [header, Salt and Saffron]

TypeScript 4.7 added optional in and out annotations with the same meaning.