What are Covariance and Contravariance?

Formally, covariance and contravariance in type systems refer to the preservation and reversal of the inheritance order for derived types. Simply put, when the type parameters of covariant entities are a parent and its descendant, the entities themselves behave as if they were parent and descendant respectively. Conversely, contravariant entities reverse this relationship, behaving as if the child and parent were switched.

These concepts are best understood through examples:
  • Covariance: A List<Integer> can be assigned to a variable of type List<? extends Number> (as if it were a subtype of List<Number>).
  • Contravariance: A Comparator<Object> can be passed as a parameter to a method List<Number>#sort that expects a Comparator<? super Number>, as if it were a supertype of Comparator<Number>.
The type relationship "can be assigned" is not exactly inheritance; such types are called compatible (relating to the "is a" relationship).

There's also a related concept called invariance, which is the absence of covariance and contravariance properties. Generics without wildcards are invariant: List<Number> cannot be assigned to a variable of type List<Double> or List<Object>.

Arrays in Java are covariant: a String[] value can be assigned to a variable of type Object[].

Since Java 5, method overriding is covariant with respect to the return type and the types of exceptions.
What are Covariance and Contravariance?