Kotlin Generics and variance
// https://kotlinlang.org/docs/reference/generics.html
// The Existential Transformation: Consumer in, Producer out! :-)
// Covariance (producer)
val covariantList: List<out Animal> = listOf<Lion>() // In Java: List<? extends Animal>
val animal: Animal = covariantList.get(0) // Ok
covariantList.put(Animal()) // Compilation error - compiler doesn't know exact
// type of covariantList and is not sure if
// it can store Animal (not all animals are lions)
// Contravariance (consumer)
val contravariantList: List<in Animal> = listOf<Any>() // In Java: List<? super Animal>
val animal: Animal = contravariantList.get(0) // Compilation error - list can store
// other objects than Animals as well
contravariantList.put(Animal()) // Ok
// Producer is covariant in T
// Class body can only return T, not take T as parameter
class Producer<out T> {
fun produce() : T // ok
fun consume(item: T) // compile error
}
// Then it's possible to:
fun covariantExample(x: Producer<String>) {
val y: Producer<Any> = x // Allowed, producer is covariant in T
}
// Consumer is contravariant in T
// Class body can only consume T (take as parameter), not return it
class Consumer<in T> {
fun produce(): T // Compilation error
fun consume(item: T) // ok
}
// Then it's possible to:
fun contravariantExample(x: Consumer<Number>) {
val y: Consumer<Double> = x // Allowed, Consumer is contravariant in T
}
// Type projection: "from" is not simply an array, but a restricted (projected)
// one: we can only call those methods that return the type parameter T,
// in this case it means that we can only call get()
fun <T> copy(from: Array<out T>, to: Array<in T>) {
// ...
}