Understanding Variance in Kotlin Generics
What is Variance?
Variance is a concept in type theory that deals with how subtyping between more complex types relates to subtyping between their components. In Kotlin, variance is primarily concerned with generics and allows developers to define how type parameters behave in relation to their subtypes. There are three types of variance: covariance, contravariance, and invariance.
Covariance
Covariance allows a generic type to be a subtype of another generic type when its type parameter is a subtype of the other type's parameter. In Kotlin, covariance is indicated using the keyword out
.
Example of Covariance
Consider the following example:
Here, Producer
can accept a subtype of T. For instance:
Now, Producer
can be treated as Producer
because Dog is a subtype of Animal.
Contravariance
Contravariance is the opposite of covariance. It allows a generic type to accept a supertype of its type parameter. In Kotlin, contravariance is indicated using the keyword in
.
Example of Contravariance
Consider the following example:
Here, Consumer
can accept a supertype of T. For instance:
In this case, Consumer
can also be used as Consumer
because Animal is a supertype of Dog.
Invariance
Invariance means that a generic type is not subtyped by its generic type parameters. This means that List
is not a subtype of List
even though Dog is a subtype of Animal. Invariance is the default behavior in Kotlin.
Example of Invariance
Consider the following example:
Here, Box
is not a subtype of Box
even though Dog is a subtype of Animal.
Summary
In Kotlin, understanding variance is crucial for effective use of generics. Covariance allows subtyping for producing types, contravariance allows subtyping for consuming types, and invariance means no subtyping is allowed between generics. Proper use of these concepts can lead to more flexible and reusable code.