Ключевое слово 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 и его подтипы.