Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Kotlin Dictionary
  3. Covariance / Contravariance

Covariance / Contravariance

In Kotlin generics, adding out (covariance) makes a class a read-only producer, while adding in (contravariance) makes it a write-only consumer. This enables type-safe upcasting and downcasting.

Syntax

// Covariant (out) — can be used as a supertype of T (read-only / Producer)
class Producer<out T>(val value: T) {
    fun get(): T = value  // OK: can return T
    // fun set(v: T) { }  // Not allowed: cannot take T as a parameter
}

// Contravariant (in) — can be used as a subtype of T (write-only / Consumer)
class Consumer<in T> {
    fun consume(value: T) { println(value) }  // OK: can take T as a parameter
    // fun get(): T { }  // Not allowed: cannot return T
}

Syntax Overview

KeywordNameDescription
out TCovariantAssignable to supertypes of T. T can only be used as a return type (Producer).
in TContravariantAssignable to subtypes of T. T can only be used as a parameter type (Consumer).
T (no variance)InvariantCannot be assigned to supertypes or subtypes. T can be used for both reading and writing.
List<out T>Use-site varianceAn example of covariance in the Kotlin standard library.

Sample Code

// Covariant (out) example — Cage<Dog> can be treated as Cage<Animal>.
open class Animal(val name: String)
class Dog(name: String) : Animal(name)
class Cat(name: String) : Animal(name)

class Cage<out T : Animal>(val animal: T) {
    fun getAnimal(): T = animal
}

// Contravariant (in) example — Trainer<Animal> can be treated as Trainer<Dog>.
interface Trainer<in T : Animal> {
    fun train(animal: T)
}

fun main() {
    // Covariant: Cage<Dog> can be assigned to Cage<Animal>.
    val dogCage: Cage<Dog> = Cage(Dog("Pochi"))
    val animalCage: Cage<Animal> = dogCage  // OK because of out
    println(animalCage.getAnimal().name)  // Pochi

    // Contravariant: Trainer<Animal> can be assigned to Trainer<Dog>.
    val animalTrainer: Trainer<Animal> = object : Trainer<Animal> {
        override fun train(animal: Animal) {
            println("Training ${animal.name}")
        }
    }
    val dogTrainer: Trainer<Dog> = animalTrainer  // OK because of in
    dogTrainer.train(Dog("Koro"))  // Training Koro

    // Kotlin's standard List uses out T (covariant)
    val dogs: List<Dog> = listOf(Dog("A"), Dog("B"))
    val animals: List<Animal> = dogs  // OK
    println(animals.map { it.name })  // [A, B]
}

Notes

Invariant generics (no variance annotation) are the most restrictive — you cannot assign Box<Dog> to Box<Animal>. A covariant class with out acts as a "producer" that only reads values, while a contravariant class with in acts as a "consumer" that only writes values.

In the Kotlin standard library, List<out E> is the canonical example of covariance. Because it is read-only, it can be assigned to a supertype. In contrast, MutableList<E> is invariant and cannot be assigned this way.

For the basics of generics, see Generics — Basics. For retaining type information at runtime, see Reified Type Parameters.

If you find any errors or copyright issues, please .