In Java, covariance allows the API designer to specify that an instance may be generalised as a certain type or any of that type's subtypes. For example:
List<? extends Shape> shapes = new ArrayList<Circle>();
// where type Circle extends Shape
Contravariance goes the other way. It allows us to specify that an instance may be generalised as a certain type or supertype.
List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry
How is Java generic's contravariance useful? When would you choose to use it?
Well, your second example would allow you to write:
Shape shape = getShapeFromSomewhere();
shapes.add(shape);
whereas you couldn't do that with the first form. It's not useful as often as covariance, I'll grant you.
One area where it can be useful is in terms of comparisons. For example, consider:
class AreaComparer implements Comparator<Shape>
...
You can use that to compare any two shapes... so it would be nice if we could also use it to sort a List<Circle>
for example. Fortunately, we can do that with contravariance, which is why there's an overload for Collections.sort
of:
public static <T> void sort(List<T> list, Comparator<? super T> c)