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.
