Covariance and Contravariance - nus-cs2030/2122-s1 GitHub Wiki
An easy-to-remember (and extremely informal) definition of covariance and contravariance is (source):
- Covariance: accept subtypes
- Contravariance: accept supertypes
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)
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)