liveBook · Manning

Covariance

As explained in Kotlin in Action, 2E

Covariance is a generic type relationship in which subtyping between parameterized types preserves the subtyping relationship between their type arguments.

Covariance lets a generic type preserve the subtype relationship of its type argument. If Cat is an Animal, a covariant Herd<Cat> can be used where Herd<Animal> is expected. This is useful for read-only, producer-style APIs: callers can retrieve values without changing the collection. section 11.3.3 section 11.3.2

Making a herd usable by a broader function

An invariant Herd<T> rejects Herd<Cat> when feedAll expects Herd<Animal>, even though every cat is an animal. Declaring the parameter as out T changes that relationship: class Herd<out T : Animal> { /* ... */ } fun feedAll(animals: Herd<Animal>) { /* feed each animal */ } fun takeCareOfCats(cats: Herd<Cat>) { feedAll(cats) } The call is safe because Herd exposes animals as produced values rather than accepting replacements. section 11.3.3

interface Producer<out T> {    #1
    fun produce(): T
}

Use out on a type parameter when the interface produces T. A Producer<Cat> can then stand in for a Producer<Animal>. section 11.3.3

A covariant type parameter follows these rules:

  • Write out before the type parameter, as in Producer<out T> or Herd<out T>. section 11.3.3
  • Use T only in out positions: return values and other produced results, not function parameter types that consume T. section 11.3.3
  • Prefer covariance for read-only abstractions such as Kotlin’s List; MutableList remains invariant because mutation would make covariance unsafe. section 11.3.2 section 11.3.3
  • Avoid fixing an invariant mismatch with an explicit cast; changing the API variance is safer when the API truly only produces T. section 11.3.3

Covariance is a compile-time subtyping rule, not a conversion of contained values. It describes whether generic instances with different type arguments can be substituted. Kotlin supports declaration-site variance with out; it also distinguishes the opposite direction, contravariance, for parameters used only in input positions. section 11.4 section 11.3.3

For example, Kotlin’s read-only List is covariant, so List<String> is a subtype of List<Any>. This differs from invariant generic classes, where changing String to Any normally creates an unrelated generic type. section 11.3.2 section 11.4

How Kotlin in Action, 2E covers it

How deep it goes
Defines preserved subtyping and the out-position safety rule, while only summarizing use-site variance.
Examples
Uses Producer<out T>, Herd<Cat> passed to Herd<Animal>, and Kotlin’s read-only List.
What it assumes
Builds from ordinary subtypes and invariant generic classes before introducing covariant declarations.
What you can do after
Apply it by marking producer-only generic type parameters out and recognizing safe subtype substitution.
How it connects
Connects subtyping, generic classes, Kotlin List, declaration-site variance, and contravariance.

Also explained in

Compare how 3 books explain covariance →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.