Type Hierarchy
open class Fruit()
open class CitrusFruit : Fruit()
class Orange : CitrusFruit()
Declaration-site Variance
The Crate
is used as a producer or consumer of Fruit
s.
Invariant class
class Crate<T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer allowed
fun last(): T = elements.last() // Producer allowed
}
Covariant classout
class Crate<out T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer not allowed: Error
fun last(): T = elements.last() // Producer allowed
}
Contravariant classin
class Crate<in T>(private val elements: MutableList<T>) {
fun add(t: T) = elements.add(t) // Consumer allowed
fun last(): T = elements.last() // Producer not allowed: Error
}
Use-site Variance
All these use-site projections are for the invariant class Crate<T>
defined above.
No Projection
No subtyping allowed: Only the Crate<Fruit>
can be assigned to a Crate<Fruit>
.
fun main() {
val invariantCrate: Crate<Fruit> = Crate<Fruit>(mutableListOf(Fruit(), Orange()))
invariantCrate.add(Orange()) // Consumer allowed
invariantCrate.last() // Producer allowed
}
Covariant Projectionout
Subtyping allowed: Crate<CitrusFruit>
can be assigned to Crate<Fruit>
when CitrusFruit
is a subtype of Fruit
.
fun main() {
val covariantCrate: Crate<out Fruit> = Crate<CitrusFruit>(mutableListOf(Orange()))
covariantCrate.add(Orange()) // Consumer not allowed: Error
covariantCrate.last() // Producer allowed
}
Contravariant Projectionin
Subtyping allowed: Crate<CitrusFruit>
can be assigned to Crate<Orange>
when the CitrusFruit
is a supertype of Orange
.
fun main() {
val contravariantCrate: Crate<in Orange> = Crate<CitrusFruit>(mutableListOf(Orange()))
contravariantCrate.add(Orange()) // Consumer allowed
contravariantCrate.last() // Producer allowed: No Error?
}
Questions
Is my understanding and the use of type projection correct in the given example?
For contravariance: why is the
last()
(producer) function not allowed at declaration-site but allowed at use-site? Shouldn't the compiler show an error like it shows in the declaration-site example? Maybe I'm missing something? If the producer is allowed for contravariance only at use-site, what could be the use case for it?
I prefer detailed answers with examples but any kind input will be much appreciated.