Чем в Java List <Object> отличается от List <? расширяет Object>?

(Примечание: это нет то же самое, что этот вопрос о vs.!)

Меня смущают дженерики Java.

ArrayList<? extends Object> x = new ArrayList<String>();
ArrayList<Object> y = new ArrayList<String>();

В этом примере первая строка компилируется, а вторая - нет. Это почему?

Я думал, что назначение формы List<A> x = new ArrayList<B>() должно быть действительным, пока B extends A, т.е. правая сторона имеет более высокую специфичность, чем левая часть назначения, но, видимо, я ошибаюсь.

Может ли кто-нибудь уточнить сходство и различие этих утверждений?

Не думаю, что это дубликат. Я уже сталкивался с этой веткой раньше при поиске, но здесь речь не идет о <?> Vs. <? расширяет Object>.

Chris O. 12.11.2018 15:12

Это также хорошая тема для чтения о коллекциях и дженериках: stackoverflow.com/questions/2723397/…

Thilo 12.11.2018 15:31
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
2
925
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

String abc = "abc";
Object x = abc;

работает, но

List<Object> list = new ArrayList<String>();

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

Обратите внимание, что на стороне объявления лучше использовать List (а не ArrayList), потому что он позволяет вам изменить реализацию на более позднем этапе, например к LinkedList.

Почему тогда Object obj = new String(); работает? Есть ли логика и осознанное дизайнерское решение, стоящее за тем фактом, что общие выражения не работают одинаково, или это просто исторически выросшие несоответствия?

Chris O. 12.11.2018 15:16

@ChrisOffner Object obj = new String(); работает, потому что каждая строка также является объектом. Но List<Object> также не является List<String>, потому что вы можете поместить Long в List<Object>, но вы не можете вставить Long в List<String>. Вот почему компилятор не допускает этого присвоения.

Thilo 12.11.2018 15:28

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

ArrayList<Object> y = new ArrayList<String>();
ArrayList<String> y2 = new ArrayList<Object>();

Если вы используете подстановочный знак ?, вы получите еще несколько вариантов, которые будут отличаться, но связаны типами наследования (String неявно расширяет Object):

ArrayList<? extends Object> x = new ArrayList<String>();

Также рассмотрите возможность использования алмазный оператор

ArrayList<Object> y = new ArrayList<>();

You can replace the type arguments required to invoke the constructor of a generic class with an empty set of type parameters (<>) as long as the compiler can infer the type arguments from the context. This pair of angle brackets is informally called the diamond.

Ответ принят как подходящий

В Java аргументы типа должны точно совпадать.

Рассмотрим следующий фрагмент кода:

ArrayList<Object> list = new ArrayList<String>();
list.add(new SomeClass());

Теперь нам удалось добавить нестроковую строку в ArrayList<String>. Во втором утверждении нет ничего плохого: мы добавляем какой-то объект в ArrayList<Object>. Значит, с первым утверждением что-то не так! Вот почему Java не позволяет вам этого делать.

Если вы измените фрагмент на:

ArrayList<? extends Object> list = new ArrayList<String>();
list.add(new SomeClass());

вы заметите, что ошибка теперь во второй строке. Вы не можете добавить объект в список.

Разница между ними заключается в следующем:

  • ArrayList<Object> означает «список ArrayList, содержащий Object». Такой список, конечно, также может содержать экземпляры подклассов Object. Поскольку каждый объект является экземпляром Object, этот список может содержать любой объект (в данном случае).

  • ArrayList<? extends Object> означает «список ArrayList, содержащий экземпляры неизвестного подкласса Object». Такой ArrayList не может содержать каждый объект. К нему могут быть добавлены только экземпляры этого неизвестного подкласса. Поскольку конкретный подкласс неизвестен, добавить к нему элементы здесь невозможно. (Но в другом месте тот же список может быть известен как ArrayList<String>, и там, конечно, можно добавлять в него элементы.)

К вашему первому примеру - но Object[] z = new Integer[3];, а затем z[2] = "ALP3"; все компилируются нормально. Я просто получаю исключение ArrayStoreException во время выполнения.

Chris O. 12.11.2018 15:28

Ваши два пункта прекрасно прояснили для меня различие. Большое тебе спасибо! :)

Chris O. 12.11.2018 15:31

@ChrisOffner. Да, массивы появились раньше дженериков и работают иначе. В частности, String[] рассматривается как подтип Object[], но List<String> не является подтипом List<Object>.

Hoopje 13.11.2018 23:27

Другие вопросы по теме