Declaration-Site Variance: in and out
Use covariant (out) and contravariant (in) type parameters correctly.
The Variance Problem
In Kotlin, List<String> is a subtype of List<Any> because List is declared with out T. Without variance annotations, this would not hold.
val strings: List<String> = listOf("a", "b")
val anys: List<Any> = strings // OK because List<out T>
// MutableList<String> is NOT a subtype of MutableList<Any>:
// val m: MutableList<Any> = mutableListOf("x") // compile error
fun main() { println(anys) }Covariance with out
out T means the class can only produce T values (return them), never consume them. This makes Producer<Dog> a subtype of Producer<Animal>.
interface Producer<out T> {
fun produce(): T
}
class DogProducer : Producer<String> {
override fun produce() = "Woof!"
}
fun sound(p: Producer<Any>) = println(p.produce())
fun main() {
val dog = DogProducer()
sound(dog) // OK: Producer<String> is a subtype of Producer<Any>
}All lessons in this course
- Generic Functions and Type Constraints with where
- Declaration-Site Variance: in and out
- Star Projection and When to Use *
- Type Erasure and reified Type Parameters