Дженерики в коллекции

Вот моя программа. Я не уверен, почему я получаю ошибку времени компиляции.

import java.util.ArrayList;
import java.util.List;

public class Test {
public static void main(String[] args) {
    List< ? extends Number > list = new ArrayList<Integer>();

    list.add(6); // Compile Time Error

    System.out.println(list);

  }
}

Но следующая программа отлично работает

import java.util.ArrayList;
import java.util.List;

public class Test {
public static void main(String[] args) {
    List< ? super Number > list = new ArrayList<Number>();

    list.add(6); 

    System.out.println(list);

    }
}

Ошибка с Eclipse:

Вот описание ошибки из Eclipse:

Метод add (int, capture # 1-of? extends Number) в типе List is not  применимо для аргументов (int)

Ответы

Ответ 1

Это потому, что то, что вы делаете в первом случае , не безопасно для типов. Вы объявили list как list для "некоторого подкласса Number", а затем попытались вставить в него Integer. Нет абсолютно никакой гарантии, что Integer совместим с фактическим типом времени выполнения базового списка. Компилятор останавливает вас здесь, потому что то, что вы делаете , не имеет смысла.

Рассмотрим в качестве крайнего примера:

List< ? extends Object > list = new ArrayList<Integer>();
list.add("Hello, World!");

Если это сработает, у вас будет List<Integer> с String в нем!

Если вы действительно хотите, чтобы он работал, вы должны сообщить компилятору, что знаете, что делаете, кастинг:

((List<Integer>)list).add(6);

Но даже тогда вы все равно получите предупреждение о безопасности типов.

Второй случай работает, потому что в списке гарантируется "некоторый суперкласс из числа". Integer является подклассом Number, поэтому он может быть неявно преобразован в любой суперкласс (включая сам Number), поэтому нет риска, что это значение несовместимо с фактическим типом списка.

Для получения дополнительной информации вы можете прочитать разницу между ковариацией и контравариантностью.

Ответ 2

Вы не можете добавить что-либо к List, у которого есть ? extends ... как часть своего типового типа.

Посмотрите на это:

List< ? extends Number > list = new ArrayList<Integer>();

Обратите внимание, что фактический List, который вы используете, - это ArrayList<Integer>. Невозможно добавить что-то еще в этот список, который не является Integer. Тем не менее, тип List<? extends Number> позволит вам добавить в список, например, Double, потому что Double также расширяет Number.

Подробнее об этом см. в разделе "Часто задаваемые вопросы по Java Generics" в Angelika Langer: Какие методы и поля доступны/недоступны через ссылочную переменную с параметризованным типом подстановочного знака?

Ответ 4

Это не работает:

List< ? extends Number > list = new ArrayList<Integer>();
list.add(6); // Compile Time Error

потому что если бы это произошло, вы могли бы прийти в такую ​​ситуацию.

class Apple extends Number  {}

List< ? extends Number > list = new ArrayList<Integer>();
list.add(new Apple()); //if it were to work, you'd have apples in what you'd think was a list of integers