How would contravariance be used in Java generics?

走远了吗. 提交于 2019-11-26 22:19:55

问题


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?


回答1:


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)



回答2:


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;
}



回答3:


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
}


来源:https://stackoverflow.com/questions/3861132/how-would-contravariance-be-used-in-java-generics

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!