A variance annotation is a modifier applied to a type parameter or type argument of a generic, in order to declare its variance.
Kotlin defines two variance annotations in its grammar: out and in.
out
The out
modifier is used to declare a type parameter as being covariant. For example:
class Box<out T>(private val item: T) {
fun getItem(): T = item
}
With the out
variance annotation declared on the type parameter T
in this class, subtyping rules can now be applied to Box
es of different types. For example:
val integer: Box<Int> = Box(1)
val number: Box<Number> = integer
Notice how number
, although declared as a Box<Number>
, refers to an object of type Box<Int>
. In other words, Box<Int>
is now a subtype of Box<Number>
.
Also note that in our class, T
is only ever used in the “out” position - it’s only ever returned from a function. By declaring it to be out
, the compiler will no longer allow the class to accept a function argument of type T
(other than within the constructor).
in
The in
modifier is used to declare a type parameter as being contravariant. For example:
class Box<in T>(private var item: T) {
fun setItem(item: T) {
this.item = item
}
}
The in
variance annotation on type parameter T
causes subtyping rules to apply to Box
in the opposite direction to the subtyping rules of the classes that it wraps. For example:
val number: Box<Number> = Box(1)
val integer: Box<Int> = number
val double: Box<Double> = number
Because of the in
modifier, Box<Number>
is now a subtype of both Box<Int>
and Box<Double>
.
By adding in
to the type parameter, the compiler will only ever allow us to use the type parameter in the “in” position - as a function argument. It can never be returned by a function.
No variance annotation
If no variance annotation is declared on a type parameter, then by default, the it’ll be invariant. For example:
class Box<T>(private var item: T) {
fun getItem(): T = item
fun setItem(item: T) {
this.item = item
}
}
In this case, no subtyping rules will be applied to Box
, so neither Box<Number>
nor Box<Int>
is a subtype of the other.