Кто-то может объяснить, что делает <? супер T> средний и когда это должно использоваться и как эта конструкция должна сотрудничать с <T> и <? расширяется T>?

Я использую довольно долгое время дженериков, но я никогда не использовал конструкцию как List<? super T>.

Что это значит? Как использовать его? Как это заботится о стирании?

Я также задаюсь вопросом: это - что-то стандартное в универсальном программировании (шаблонное программирование?) или это - просто Java 'изобретение'? c#, например, позволяют подобные конструкции?

18
задан Roman 5 April 2010 в 13:09
поделиться

4 ответа

Эта конструкция используется, когда вы хотите использовать элементы из коллекции в другую коллекцию. Например, у вас есть общий Stack, и вы хотите добавить метод popAll, который принимает в качестве параметра коллекцию Collection и забирает в нее все элементы из стека. По здравому смыслу, этот код должен быть законным:

Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ... ;
numberStack.popAll(objects);

но он компилируется, только если вы определите popAll следующим образом:

// Wildcard type for parameter that serves as an E consumer
public void popAll(Collection<? super E> dst) {
    while (!isEmpty())
    dst.add(pop());
}

Другая сторона медали в том, что pushAll должен быть определен следующим образом:

// Wildcard type for parameter that serves as an E producer
public void pushAll(Iterable<? extends E> src) {
    for (E e : src)
    push(e);
}

Обновление: Джош Блох распространил эту мнемонику, чтобы помочь вам запомнить, какой тип подстановочного знака использовать:

PECS означает производитель-расширяет, потребитель-супер.

Подробнее см. в Effective Java 2nd Ed., Item 28.

10
ответ дан 30 November 2019 в 09:11
поделиться

Это называется «ограниченный подстановочный знак». Это очень хорошо объяснено в официальном руководстве .

Как указано в руководстве, вы знаете, что список содержит объекты ровно одного подтипа T

Например, List может содержать только Integer s или только Long s, но не то и другое одновременно.

4
ответ дан 30 November 2019 в 09:11
поделиться

В FAQ по Java Generics есть хорошее объяснение о Java generics. Проверьте вопрос Что такое ограниченный подстановочный знак?, в котором подробно объясняется использование конструкции "? super T".

0
ответ дан 30 November 2019 в 09:11
поделиться

Эти вещи известны в теории типов как variance, причем является ко-вариантной нотацией, а - контравариантной нотацией. Самое простое объяснение состоит в том, что ? можно заменить любым типом, расширяющим T в ко-инвариантной нотации, а ? можно заменить любым типом, который T расширяет в контравариантной.

Использование ко- и контравариантности гораздо сложнее, чем может показаться на первый взгляд, особенно потому, что дисперсия "переключается" в зависимости от позиции.

Простым примером может служить класс функций. Скажем, у вас есть функция, которая принимает A и возвращает B. Правильной нотацией для этого будет сказать, что A является контравариантной, а B - ковариантной. Чтобы лучше понять, как это происходит, рассмотрим метод - назовем его g - который получает этот гипотетический класс function, где f должен получать Arc2D и возвращать Shape.

Внутри g эта f вызывается, передавая Arc2D, а возвращаемое значение используется для инициализации Area (которая ожидает Shape).

Теперь предположим, что переданная вами f принимает любую Shape и возвращает Rectangle2D. Поскольку Arc2D также является Shape, то g не получит ошибку при передаче Arc2D в f, а поскольку Rectangle2D также является Shape, то его можно передать конструктору Area.

Если вы попытаетесь инвертировать любую из вариаций или поменять местами ожидаемый и фактический типы в этом примере, вы увидите, что это не удастся. У меня сейчас нет времени, чтобы записать этот код, и моя Java довольно ржавая, но я посмотрю, что я смогу сделать позже - если никто не будет достаточно добр, чтобы сделать это первым.

3
ответ дан 30 November 2019 в 09:11
поделиться