Covariance and Contravariance
A covariant type operator in a type system preserves the ≤ ordering of types. A contravariant operator reverses ≤. If none of these apply, the operator is invariant.
The term covariant change is used when a type moves down the type hierarchy, in the same direction as the child class. The term contravariant is used for the opposite, when a type moves up the class hierarchy, in the opposite direction as subclassing.
Consider the following class hierarchy,
Consider the following code,
Parent p = new Child(); Mammal m = p.func(new Monkey());
The impact of covariant is in the argument. As far as the compiler is concerned, the argument is perfectly acceptable, as it matches with the Parent class signature. But, at run-time it invokes Child class method which throw the exception since it cannot accept the Monkey instance.
The impact of contravariant is in the reutn type. The return type of Mammal is acceptable by compiler, but at run-time the function returns the instance of Animal, which will cause exception.
Covariance and Contravariance: Conflict without a Cause
Covariance and contravariance characterize two completely distinct mechanisms: subtyping and specialization. The so-called contravariance rule correctly captures the subtyping relation (that relation which establishes which sets of functions can replace another given set in every context). A covariant relation, instead, characterizes the specialization of code (i.e., the definition of new code which replaces old definitions in some particular cases). Therefore, covariance and contravariance are not opposing views, but distinct concepts that each have their place in object-oriented systems.
One has to use contravariance when static type safety is required, but otherwise covariance is more natural, flexible, and expressive.
<< Back to Tech Archives |