Variance is the category of possible relationships between more complex types and their components’ subtypes.1
The main variance concepts are:
Invariance
A generic type G<T> only accepts the exact type T, not subtypes or supertypes.
This is the default in Python’s list without specific type hints and in Java’s generics without wildcards.
Covariance
A generic type G<T> allows subtypes of T.
If B is a subtype of A, then G<B> is a subtype of G<A>.
Useful for read-only operations.
For example, “list of Cat” is a subtype of “list of Animal” because the list type constructor is covariant.1
Example in Python: List[Derived] can be treated as List[Base] with covariant typing (using typing.List with covariant=True in type hints).
Contravariance
A generic type G<T> allows supertypes of T.
If B is a subtype of A, then G<A> is a subtype of G<B>.
Useful for write-only operations.
For example, “function (Animal) → String” is a subtype of “function (Cat) → String” because the function type constructor is contravariant in the parameter type.1
Example in Python: Contravariant types are less common but can be defined in typing.Generic with contravariant=True.
Bivariance
A rare case where a generic type accepts both subtypes and supertypes (not supported in Java or Python, but seen in some dynamic languages).