разница между созданием неограниченного и ограниченного массива подстановочных знаков?

Почему этот код действителен

ArrayList<?>[] arr = new ArrayList<?>[2];

а следующие два нет?

ArrayList<? extends Object>[] arr = new ArrayList<? extends Object>[2];
ArrayList<? super Object>[] arr = new ArrayList<? super Object>[2];

Две последние строки генерируют ошибку компиляции;

ошибка: создание универсального массива.

Пожалуйста, поясните разницу.

Обновить

С другой стороны, ArrayList<?>[] arr = new ArrayList<?>[2]; компилируется хорошо, но

ArrayList<?> arr = new ArrayList<?>();

нет.


person gstackoverflow    schedule 01.10.2014    source источник


Ответы (2)


Здесь есть несколько проблем, давайте рассмотрим каждую по очереди:

  1. Связанный тип (т.е. extends Object) может быть объявлен только при объявлении типа, его нельзя использовать при создании экземпляра объекта.

    Например

    ArrayList<? extends Object> ab = new ArrayList<? extends Object>(); // error ArrayList<? extends Object> ac = new ArrayList<String>(); // okay

  2. Массивы не поддерживают параметры типа, например:

    List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile time error List<Integer> list = new List<Integer>(); // okay

    Oracle документирует причины этого ограничения здесь.

  3. <?> можно использовать при объявлении параметра типа и с массивами. Он был добавлен, чтобы помочь избежать ошибок «непроверенное исключение» при смешивании кода Java, который использует и не использует дженерики. Это означает «неизвестный общий тип». Подробнее о неограниченных подстановочных знаках здесь.

    ArrayList<?>[] arr = new ArrayList<?>[2];

    является действительным по причинам, изложенным выше. Однако он имеет очень ограниченное применение, поскольку только null может быть присвоен типам, объявленным как <?>.

    arr[0] = null; // compiles

    arr[1] = new Object(); // compile time error

    Oracle предоставляет следующее руководство по использованию подстановочных знаков, которое поможет понять, когда использовать эти подстановочные знаки. карты.

  4. <?> нельзя использовать для создания экземпляра объекта. Например

    ArrayList<?> arr = new ArrayList<?>(); // does not compile

    ArrayList<?> arr2 = new ArrayList<>(); // but this does

    ArrayList<?> arr3 = new ArrayList<String>(); // and so does this

    Однако по-прежнему существует проблема, заключающаяся в том, что использование <?> принимает только null.

    arr3.add( " " ); // does not compile even though it was instantiated with String

    arr3.add( null ); // compiles just fine

person Chris K    schedule 01.10.2014
comment
+1 - Я не рассматривал взаимодействие с массивами. Только само объявление типа. - person Rudi Kershaw; 01.10.2014
comment
Возможно, первый вопрос должен быть, почему следующее не компилируется: - нет, я это знаю - person gstackoverflow; 01.10.2014
comment
@gstackoverflow Извините, я не следил за вами. Что ты имел в виду? - person Chris K; 01.10.2014
comment
@gstackoverflow пингуйте меня, когда у вас будет возможность впитать, я могу работать над любыми разъяснениями. Однако ключевым моментом является то, что «Java не поддерживает общие массивы». Период. Наличие ArrayList<?>[] работы было в некотором смысле отвлекающим маневром, и оно существует как особый случай интеграции кода, использующего дженерики, с кодом, который их не использует. - person Chris K; 01.10.2014
comment
Ниже приведен отличный учебник: docs.oracle.com/javase/tutorial. /java/generics/index.html - person Chris K; 01.10.2014
comment
@Chris K ArrayList‹?› arr = new ArrayList‹?›; он не компилируется без скобок массива. - person gstackoverflow; 01.10.2014
comment
@gstackoverflow, пожалуйста, перепроверьте, я думаю, что меня поймало автоматическое форматирование stackoverflows. Я добавил кавычки, чтобы он не форматировал синтаксис Java. - person Chris K; 01.10.2014
comment
@Chris K не понял причинно-следственной связи после слова Таким образом - person gstackoverflow; 01.10.2014
comment
@gstackoverflow Я удалил слово «таким образом» и изменил предложение, чтобы сделать его более понятным. - person Chris K; 01.10.2014
comment
@Chris K, мое текущее мнение: в целом неограниченный подстановочный знак не ограничивает тип, поэтому массив не должен проверять тип перед добавлением элемента, например, в массив, поэтому мы не можем получить ArrayStoredException. Но ограниченный тип подстановочных знаков, таким образом, поскольку мы используем массив, мы должны знать этот тип во время выполнения, но это невозможно для дженериков. - person gstackoverflow; 02.10.2014
comment
@Chris K Что ты думаешь об этом? - person gstackoverflow; 02.10.2014
comment
@gstackoverflow Я думаю, что вы в значительной степени там. Однако неограниченный подстановочный знак не означает «не ограничивает тип», это означает «неизвестный тип». Есть разница. Что касается массивов, которые должны знать тип во время выполнения, да, язык может быть спроектирован так, чтобы знать. Однако, как я думаю, вы сказали «невозможно для дженериков», Java не поддерживает такое поведение. В общем, Java Generics — это большой мешок компромиссов, в основном прагматических. - person Chris K; 02.10.2014

Вы должны сначала понять, почему создание массива параметризованного типа не разрешено. Это потому, что массивы проверяют во время выполнения, что вставленные элементы являются экземплярами типа компонента (а-ля instanceof). Невозможно проверить instanceof параметризованный тип, поскольку объект не имеет представления о параметре типа, с которым он был создан. instanceof ArrayList<Integer> недопустимо в Java, как и что-то вроде instanceof ArrayList<? extends Number>, но instanceof ArrayList<?> разрешено в Java, потому что ему не нужна информация о параметре типа объекта. (Кстати, instanceof ArrayList<? extends Object> и instanceof ArrayList<? super Object> тоже запрещены.)

Концептуально ArrayList<? extends Object> почти полностью идентичен ArrayList<?> (есть небольшие различия, но они не существенны), но для согласованности грамматики new ArrayList<? extends X>[...] не допускается ни для одного X.

person newacct    schedule 02.10.2014