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).

Footnotes

  1. https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science) 2 3