29

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?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Synesso
  • 37,610
  • 35
  • 136
  • 207

3 Answers3

36

Here's a relevant excerpt from Java Generics and Collections:

2.4. The Get and Put Principle

It may be good practice to insert wildcards whenever possible, but how do you decide which wildcard to use? Where should you use extends, where should you use super, and where is it inappropriate to use a wildcard at all?

Fortunately, a simple principle determines which is appropriate.

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.

We already saw this principle at work in the signature of the copy method:

public static <T> void copy(List<? super T> dest, List<? extends T> src)

The method gets values out of the source src, so it is declared with an extends wildcard, and it puts values into the destination dst, so it is declared with a super wildcard. Whenever you use an iterator, you get values out of a structure, so use an extends wildcard. Here is a method that takes a collection of numbers, converts each to a double, and sums them up:

public static double sum(Collection<? extends Number> nums) {
    double s = 0.0;
    for (Number num : nums) s += num.doubleValue();
    return s;
}
Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
34

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)
Abel
  • 56,041
  • 24
  • 146
  • 247
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Does this mean I could call Collections.sort(shapes, new Comparator() { ... }); ? Object is super Shape>, right? – Synesso Oct 05 '10 at 06:42
  • 1
    @Synesso: Yup, if you've got a comparator which can compare any two Objects, that can compare any two Shapes, so it's safe. – Jon Skeet Oct 05 '10 at 06:44
  • **@JonSkeet:** still, I do not get why we need explicitly `super` in the second case. I understand that using `extends` will not help to add any Shape to the List, but `List shapes = new ArrayList();?` would have equally worked as well, right? I do not find the [documentation](https://docs.oracle.com/javase/tutorial/java/generics/lowerBounded.html) from Oracle useful either to fully grasp the usefulness of `super` in this case :-( – senseiwu May 30 '16 at 14:25
  • @zencv: No, because an `ArrayList` *isn't* a `List`. A `List` has a `get` method such that the return value will always be a `Shape` reference or `null` - whereas an `ArrayList` can contain elements which aren't shapes. – Jon Skeet May 30 '16 at 21:56
  • @JonSkeet: sorry, I meant to write `List shapes = new ArrayList();` instead of `List shapes = new ArrayList();`. I get why the latter won't work, but I am still not fully sure why writing `? super Shape` is of any benefit when you can just write `Geometry`; especially given that Java doesn't support multiple inheritance. Only scenario I can imagine is when `Shape` implements more than one interface, then we can write either `List super Shape> shapes = new ArrayList();` or `List super Shape> shapes = new ArrayList();` . Thanks!! – senseiwu May 31 '16 at 10:37
  • 1
    @zencv: The point is that if you want to write a method which will add a bunch of `Shape` elements to an existing list, you don't mind whether that's a `List` or a `List` - so you could have a method parameter of `List super Shape> listToAddTo` for example. – Jon Skeet May 31 '16 at 14:34
  • 1
    @Joschua: That's not what that means - it means "It's a list of some type, and that type extends Shape". So it could be (say) a `List`, and you shouldn't be able to add a `Circle` to that. A "list that contains elements which are Shape or extend from it" is just a `List`. – Jon Skeet Jul 28 '19 at 20:09
8

For example, when implementing the Collections.addAll() method, you need a collection that can contain some type T or a supertype of T. The method then looks like:

public static <T> void addAll(Collection<? super T> collection, T... objects) {
    // Do something
}
Marc
  • 3,550
  • 22
  • 28
  • Demonstration code (useless IRL): `List l = List.of();` `Collections.addAll(l, new Cow(), new Cow());` Thanks to the contravariance we can add cows in a list of animals. – ARno Jun 02 '21 at 13:16