(Примечание: это нет то же самое, что этот вопрос о vs.!)
Меня смущают дженерики Java.
ArrayList<? extends Object> x = new ArrayList<String>();
ArrayList<Object> y = new ArrayList<String>();
В этом примере первая строка компилируется, а вторая - нет. Это почему?
Я думал, что назначение формы List<A> x = new ArrayList<B>() должно быть действительным, пока B extends A, т.е. правая сторона имеет более высокую специфичность, чем левая часть назначения, но, видимо, я ошибаюсь.
Может ли кто-нибудь уточнить сходство и различие этих утверждений?
Это также хорошая тема для чтения о коллекциях и дженериках: stackoverflow.com/questions/2723397/…




String abc = "abc";
Object x = abc;
работает, но
List<Object> list = new ArrayList<String>();
нет, потому что общие выражения должны точно соответствовать, если они не содержат подстановочный знак.
Обратите внимание, что на стороне объявления лучше использовать List (а не ArrayList), потому что он позволяет вам изменить реализацию на более позднем этапе, например к LinkedList.
Почему тогда Object obj = new String(); работает? Есть ли логика и осознанное дизайнерское решение, стоящее за тем фактом, что общие выражения не работают одинаково, или это просто исторически выросшие несоответствия?
@ChrisOffner Object obj = new String(); работает, потому что каждая строка также является объектом. Но List<Object> также не является List<String>, потому что вы можете поместить Long в List<Object>, но вы не можете вставить Long в List<String>. Вот почему компилятор не допускает этого присвоения.
Оба приведенных ниже случая не будут работать, потому что вы инициализируете другой тип объекта, чем ожидалось:
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 во время выполнения.
Ваши два пункта прекрасно прояснили для меня различие. Большое тебе спасибо! :)
@ChrisOffner. Да, массивы появились раньше дженериков и работают иначе. В частности, String[] рассматривается как подтип Object[], но List<String> не является подтипом List<Object>.
Не думаю, что это дубликат. Я уже сталкивался с этой веткой раньше при поиске, но здесь речь не идет о <?> Vs. <? расширяет Object>.