Skip to content

Covariance and Contravariance

Russell Saerang edited this page Nov 24, 2021 · 3 revisions

Definition

An easy-to-remember (and extremely informal) definition of covariance and contravariance is (source):

  • Covariance: accept subtypes
  • Contravariance: accept supertypes

Covariance

To achieve covariance for a list, a wildcard declared as ? extends T is used (or upper-bounded wildcard).
For example, List<? extends Number> represents a list of Number or its sub-types such as Integer and Double. Number is the 'upper-bound'.

//possible and valid list assignments from ? extends Number
List<? extends Number> f = new ArrayList<Number>();  // Number "extends" Number 
List<? extends Number> f = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> f = new ArrayList<Double>(); //Double extends Number

In covariance, we can get but we cannot put.

//getting is valid because we have an extent of guarantee that the value is Number or a subtype of Number
Number n = f.get(0); //Number is the upperbound so e.g. even if f is a list of Integers or Doubles, Number n = Integer/Double is valid 
Integer i = f.get(0); //invalid because f could be a List of Doubles and e.g. Integer i = 2.56 is invalid as they are incompatible types
Double d = f.get(0) //invalid because f could be a List of Integer and e.g. Double d = 2 is invalid as they are incompatible types

//putting is invalid because we cannot guarantee what kind of list f is actually pointing to due to the possibilities with "? extends Number"
f.add(1.0) //invalid because f could be a list of Integers
f.add(1) //invalid because f could be a list of Doubles 

By using extends, only T or subclasses of T (? extends T) can be read (e.g. using ? extends Number, using Number n = f.get(0), we can read Numbers, Integers, Doubles)

Contravariance

We use a a lower-bounded wildcard: ? super T For example, List<? extends Integer> represents a list of Integer or its super-types such as Number or Object. Integer is the 'lower-bound'. In contravariance, we cannot get but we can put.

List<? super Integer> f1 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer 
List<? super Integer> f1 = new ArrayList<Number>();   // Number is a superclass of Integer
List<? super Integer> f1 = new ArrayList<Object>();   // Object is a superclass of Integer
Integer i = f1.get(0); //invalid because f1 could be pointing to a list of Number, which can include Doubles
Number n = f1.get(0); //invalid because f1 could be pointing to a list of Objects, which can include Strings
Object o = f1.get(0); // this is valid but redundant because every class is a subclass of Object

//putting is invalid because we cannot guarantee what kind of list f is actually pointing to due to the possibilities with "? extends Number"
f.add(1) //valid because f could be a list of Integers, list of Numbers or Objects but they all are compatible with Integer  

By using super, only T or subclasses of T can be added (e.g. for ? super Integer, we can only add Integer or subclasses of Integer)

Source

Link

Clone this wiki locally