Ключевое слово java generics super
Я просмотрел эти темы
Тем не менее, я по-прежнему кажусь потерянным с super
ключевым словом:
-
Когда мы объявляем такую коллекцию:
List<? super Number> list = null;
list.add(new Integer(0));//this compiles
list.add(new Object());//this doesn't compile
не должно быть обратного - у нас есть список, содержащий некоторые объекты (неизвестного типа), которые являются родителями Number
. Поэтому Object
должен соответствовать (поскольку он является родителем Number
), а Integer
не должен. По какой-то причине это происходит наоборот.
-
Если у нас есть следующий код
static void test(List<? super Number> param) {
param.add(new Integer(2));
}
public static void main(String[] args) {
ArrayList<String> sList = new ArrayList<String>();
test(sList); //will never compile, however...
}
Невозможно скомпилировать приведенный выше код (и мое здравомыслие предполагает, что это правильное поведение), но основная логика может доказать обратное:
String is Object, Object is superclass of Number. So String should work.
Я знаю, что это безумие, но разве это не причина, по которой они не допускали конструкций <S super T>
? Если да, то почему <? super T>
разрешено?
Может ли кто-нибудь помочь мне восстановить недостающую часть этой логической цепочки?
Ответы
Ответ 1
Ограниченный подстановочный знак в List<? super Number>
может захватывать Number
и любой из его супертипов. Поскольку Number extends Object implements Serializable
, это означает, что единственными типами, которые в настоящее время являются сбрасываемыми с помощью List<? super Number>
, являются:
-
List<Number>
-
List<Object>
-
List<Serializable>
Обратите внимание, что вы можете add(Integer.valueOf(0))
к любому из вышеуказанных типов. однако вы НЕ МОЖЕТЕ add(new Object())
к List<Number>
или List<Serializable>
, поскольку это нарушает правило безопасности общего типа.
Следовательно, это НЕ верно, что вы можете add
любой супертип Number
до List<? super Number>
; это просто не так, как ограниченный шаблон и конверсия захвата. Вы не объявляете List<? super Number>
, потому что можете добавить к нему Object
(вы не можете!); вы делаете, потому что хотите добавить к нему Number
объекты (т.е. это "потребитель" Number
), а просто a List<Number>
является слишком строгим.
Ссылки
См. также
- Эффективное Java 2nd Edition, Пункт 28: Используйте ограниченные подстановочные знаки для повышения гибкости API
- "PECS обозначает производителя < <2 → , потребитель-
super
Связанные вопросы
- Слишком много списка, PECS,
new Integer(0)
vs valueOf
и т.д.
Ответ 2
В первой части List<Number>
помещается List<? super Number>
, но вы не можете добавить Object
в List<Number>
. Поэтому вы не можете добавить Object
в List<? super Number>
.
С другой стороны, вы можете добавить в свой список все подклассы Number
(Number
).
Для второй части String
является Object
, но String
не является суперклассом Number
.
Если это сработало, так как каждый класс является подклассом Object
, super
не имеет смысла.
Посмотрите все возможные случаи с List<? super Number>
:
- Прошедший список - это
List<Object>
-
List<Object>
будет работать
-
Object
подходит для <? super Number>
- Вы можете добавить любой подтип
Number
в List<Object>
- Даже если вы можете добавить в него
String
, единственное, что вы уверены в том, что вы можете добавить любой подкласс Number
.
- Прошедший список -
List<Number>
:
-
List<Number>
будет работать
-
Number
подходит для <? super Number>
- Вы можете добавить любой подтип
Number
в List<Number>
- Прошедший список - это
List<Integer>
(или любой подкласс Number
):
-
List<Integer>
не будет работать
- Целое число является подклассом
Number
, поэтому мы хотим избежать
- Даже если
Integer
подходит для Number
, вам не удастся добавить какой-либо подкласс Number
в List<Integer>
(например, a Float
)
-
super
не означает подкласс.
- Прошедший список - это
List<String>
(или любой класс, не расширяющий Number
, ни в "супер иерархии" Number
(т.е. Number
и Object
):
-
List<String>
не будет работать
-
String
не подходит в Number
"супер иерархии"
- Даже если
String
подходит для Object
(который является супер-классом Number
), вы не сможете добавить Number
в List
, который содержит любой подкласс из одного суперклассов Number
)
-
super
не означает никакого подкласса одного из суперклассов, это означает только один из суперклассов.
Как это работает?
Можно сказать, что до тех пор, пока вы можете добавить какой-либо подкласс Number
с набранным List
, он соблюдает ключевое слово super
.
Ответ 3
У меня была та же проблема. Я думаю, что синтаксис является причиной путаницы.
List<? super Number>
кажется, предлагает "список вещей, которые являются супертипами числа", но на самом деле это означает " список вещей, которые имеют Number в качестве своего супертипа".
Как только я начал читать его так, он щелкнул.
Ответ 4
Я не понял это некоторое время. Многие из ответов здесь, а другие вопросы показывают, когда и где определенные обычаи являются ошибками, но не так много.
Вот как я наконец понял. Если у меня есть функция, которая добавляет Number
к List
, я могу добавить их типа MySuperEfficientNumber
, который является моим собственным пользовательским классом, который реализует Number
(но не является подклассом Integer
). Теперь вызывающий может ничего не знать о MySuperEfficientNumber
, но до тех пор, пока они знают, что обработать элементы, добавленные в список, не более, чем Number
, они будут в порядке.
Если я объявил свой метод как:
public static void addNumbersToList(List<? extends Number> numbers)
Тогда вызывающий может передать . Если мой метод добавил MySuperEfficientNumber
в конец numbers
, то у вызывающего больше не будет List
из Integer
, и следующий код не будет работать:
List<Integer> numbers = new ArrayList<Integer>();
addNumbersToList(numbers);
// The following would return a MySuperEfficientNumber not an Integer
Integer i = numbers.get(numbers.size()-1)
Очевидно, это не сработает. И ошибка будет внутри метода addNumbersToList
. Вы получите что-то вроде:
The method add... is not applicable for the arguments (MySuperEfficientNumber)
Поскольку numbers
может быть любым конкретным типом Number
, не обязательно совместимым с MySuperEfficientNumber
. Если я перевернул объявление вокруг, чтобы использовать super
, метод будет компилироваться без ошибок, но код вызывающего абонента завершится с ошибкой:
The method addNumbersToList(List<? super Number>)... is not applicable for the arguments (List<Integer>)
Потому что мой метод говорит: "Не думайте, что ваш List
может быть чем-то более конкретным, чем Number
. Я мог бы добавить в список всевозможные странные Number
, вам просто нужно будет справитесь с этим. Если вы хотите думать о них как о чем-то более общем, чем Number
- like Object
- это прекрасно, я гарантирую, что они будут как минимум Number
s, но вы можете относиться к ним больше обычно, если вы хотите.
В то время как extends
говорит: "Мне все равно, какой тип List
вы мне даете, если каждый элемент имеет хотя бы Number
. Это может быть любой тип Number
даже ваши собственные странные, пользовательские, готовые Number
s. Пока они реализуют этот интерфейс, мы хорошо. Я не собираюсь добавлять что-либо в ваш список, так как я не знаю, какой конкретный конкретный тип вы используете там."
Ответ 5
List<? super Number>
означает, что ссылочный тип переменной предполагает, что у нас есть список номеров, объектов или серийных номеров.
Причина, по которой вы не можете добавить объект, заключается в том, что компилятор не знает, какой из этих классов относится к общему определению фактического объекта-объекта, поэтому он позволяет вам передавать Number или подтипы Number, например Double, Integer и т.д.
Допустим, у нас есть метод, который возвращает List<? super Number>
. Создание объекта внутри метода инкапсулировано из нашего представления, мы просто не можем сказать, что это что-то вроде этого:
List<? super Number> returnValue = new LinkedList<Object>();
или
List<? super Number> returnValue = new ArrayList<Number>();
Таким образом, общий тип может быть Object или Number. В обоих случаях нам будет разрешено добавлять Number, но только в одном случае нам будет разрешено добавлять Object.
В этой ситуации вы должны различать ссылочный тип и тип фактического объекта.
Ответ 6
List<? super Number>
является таким List<AncestorOfNumber>
, где мы можем неявно бросать каждый Number
в его супер-тип AncestorOfNumber
.
Рассмотрим следующее: какой общий тип должен быть ????
в следующем примере?
InputStream mystream = ...;
void addTo(List<????> lsb) {
lsb.add(new BufferedInputStream(mystream));
}
List<BufferedInputStream> lb = new ArrayList<>();
List<InputStream> li = new ArrayList<>();
List<Object> lo = new ArrayList<>();
...
{ addTo(lb); addTo(li); addTo(lo); }
Ответ: ????
- это то, к чему мы можем применить BufferedInputStream
, который является тем же самым или одним из его предков: ? super BufferedInputStream
Ответ 7
Позвольте привести очень простой пример.
public void add(List<? super Number> list) {
}
это позволит эти звонки
add(new LinkedList<Number>());
и все выше номера вроде
add(new LinkedList<Object>());
но ничего ниже иерархии так нет
add(new LinkedList<Double>());
или же
add(new LinkedList<Integer>());
Таким образом, поскольку программе непонятно знать, передаете ли вы List с Number или Object, компилятор не может позволить вам добавить что-либо выше Number к нему.
Например, List не будет принимать Object, несмотря на Object, который будет принимать Number. Но так как это не ясно, единственным допустимым входным параметром будет Number и его подтипы.