Как будет использоваться контравариантность в дженериках Java? - PullRequest
24 голосов
/ 05 октября 2010

В Java ковариация позволяет разработчику API указать, что экземпляр может быть обобщен как определенный тип или любой из его подтипов. Например:

List<? extends Shape> shapes = new ArrayList<Circle>(); 
// where type Circle extends Shape

Контравариантность идет другим путем. Это позволяет нам указать, что экземпляр может быть обобщен как определенный тип или супертип.

List<? super Shape> shapes = new ArrayList<Geometry>();
// where Shape extends Geometry

Чем полезна контравариантность универсального Java-кода? Когда вы решите использовать его?

Ответы [ 3 ]

34 голосов
/ 05 октября 2010

Вот соответствующая выдержка из Обобщения и коллекции Java :

2.4.Принцип «получить и положить»

Хорошей практикой может быть вставка подстановочных знаков, когда это возможно, но как вы решаете, какой подстановочный знак использовать?Где вы должны использовать extends, где вы должны использовать super, и где вообще не следует использовать подстановочный знак?

К счастью, простой принцип определяет, что уместно.

Принцип Get и Put : используйте подстановочный знак extends, когда вы только получаете значения из структуры, используйте подстановочный знак super, когда вы только помещаете значения в структуру, и неиспользуйте подстановочный знак, когда вы получаете и ставите.

Мы уже видели этот принцип в работе в сигнатуре метода копирования:

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

Метод получает значения изsource src, поэтому он объявляется с подстановочным знаком extends и помещает значения в целевую dst, поэтому он объявляется с подстановочным знаком super.Всякий раз, когда вы используете итератор, вы получаете значения из структуры, поэтому используйте подстановочный знак extends.Вот метод, который берет коллекцию чисел, преобразует каждое в двойное и суммирует их:

public static double sum(Collection<? extends Number> nums) {
    double s = 0.0;
    for (Number num : nums) s += num.doubleValue();
    return s;
}
30 голосов
/ 05 октября 2010

Хорошо, ваш второй пример позволит вам написать:

Shape shape = getShapeFromSomewhere();
shapes.add(shape);

тогда как вы не могли сделать это с первой формой. Это не так часто, как ковариация, я вам предоставлю.

Одна область, где она может быть полезной, - это сравнения. Например, рассмотрим:

class AreaComparer implements Comparator<Shape>
...

Вы можете использовать это для сравнения любых двух фигур ... поэтому было бы неплохо, если бы мы могли также использовать его для сортировки List<Circle>, например. К счастью, мы можем сделать это с контравариантностью, поэтому существует перегрузка для Collections.sort из:

public static <T> void sort(List<T> list, Comparator<? super T> c)
6 голосов
/ 05 октября 2010

Например, при реализации метода Collections.addAll () вам нужна коллекция, которая может содержать некоторый тип T или супертип T. Затем метод выглядит так:

public static <T> void addAll(Collection<? super T> collection, T... objects) {
    // Do something
}
...