Генераторы Java - инициализация ArrayList

Известно, что arraylist init. должен быть таким

ArrayList<A> a = new ArrayList<A>();
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error

так, почему java разрешает это?

1. ArrayList<? extends Object> a1 = new ArrayList<Object>();
2. ArrayList<?> a2 = new ArrayList<Integer>();

то, если они правильны, почему они не позволяют?

1. a1.add(3);
2. a2.add(3);

сообщение компилятора: метод add (int, capture # 1-of? extends Object) в типе ArrayList не применим для аргументов (int)

более общий

  1. a1.add(null e);
  2. a2.add(? e);

Я читал об этом, но хорошо слышать от вас. спасибо

Другая забавная точка:

 ArrayList<ArrayList<?>> a = new ArrayList<ArrayList<?>>(); // correct
 ArrayList<?> a = new ArrayList<?>(); // wrong. I know it reason but I have some 
question in my mind that mentioned above 

Ответ 1

Вы не можете назначить List<Number> ссылке типа List<Integer>, потому что List<Number> позволяет типы чисел, отличные от Integer. Если вам было разрешено это сделать, разрешено следующее:

List<Number> numbers = new ArrayList<Number>();
numbers.add(1.1); // add a double
List<Integer> ints = numbers;
Integer fail = ints.get(0); // ClassCastException!

Тип List<Integer> гарантирует, что все, что он содержит, будет Integer. Вот почему вы можете получить Integer из него без кастинга. Как вы можете видеть, если компилятор разрешил List другого типа, такого как Number, присваиваться List<Integer>, что гарантия будет нарушена.

Назначение a List<Integer> для ссылки типа типа List<?> или List<? extends Number> является законным, поскольку ? означает "неизвестный подтип данного типа" (где тип Object в случай только ? и Number в случае ? extends Number).

Так как ? указывает, что вы не знаете, какой конкретный тип объекта примет List, нецелесообразно добавлять к нему ничего, кроме null. Однако вам разрешено извлекать из него любой объект, который предназначен для использования ограниченного типа подстановок ? extends X. Обратите внимание, что обратное верно для ? super X ограниченного типа подстановок... a List<? super Integer> представляет собой "список неизвестного типа, который является по крайней мере супертипом Integer". Хотя вы точно не знаете, какой тип List он может (List<Integer>, List<Number>, List<Object>)), вы точно знаете, что что бы это ни было, к нему можно добавить Integer.

Наконец, new ArrayList<?>() не является законным, поскольку, когда вы создаете экземпляр парамиризованного класса, такого как ArrayList, вы должны указать определенный параметр типа. Вы действительно можете использовать что-либо в своем примере (Object, Foo, это не имеет значения), так как вы никогда не сможете добавить что-либо, кроме null, так как вы назначаете его непосредственно в ArrayList<?> ссылка.

Ответ 2

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

ArrayList<A> a = new ArrayList<A>();

Здесь a - ссылка на экземпляр определенного типа - именно список массивов a s. Более явно, a является ссылкой на список массивов, который примет a и произведет a s. new ArrayList<A>() - это экземпляр списка массивов a s, то есть списка массивов, который примет a и произведет a s.

ArrayList<Integer> a = new ArrayList<Number>(); 

Здесь a является ссылкой на точно список массивов Integers, то есть именно список массивов, который может принимать Integer и будет производить Integer s. Он не может указывать на список массивов Number s. Этот список массивов Number не может соответствовать всем promises ArrayList<Integer> a (т.е. Список массивов Number может создавать объекты, которые не являются Integer s, хотя и пустые справа).

ArrayList<Number> a = new ArrayList<Integer>(); 

Здесь объявление a говорит, что a будет ссылаться на точно список массивов Number s, то есть именно список массивов, который примет Number и будет производить Number s. Он не может указывать на список массивов Integer s, потому что объявление типа a говорит, что a может принимать любые Number, но этот список массивов Integer не может принимать только любой Number, он может принимать только Integer s.

ArrayList<? extends Object> a= new ArrayList<Object>();

Здесь a является (общей) ссылкой на семейство типов, а не ссылкой на определенный тип. Он может указывать на любой список, который является членом этого семейства. Однако компромисс для этой приятной гибкой справки заключается в том, что они не могут обещать всю функциональность, которая могла бы быть, если бы это была ссылка на конкретный тип (например, не общий). В этом случае a является ссылкой на список массивов, который будет генерировать Object s. Но, в отличие от ссылки на список типов, эта ссылка a не может принять никаких Object. (т.е. не каждый член семейства типов, для которого a может указывать, может принимать любые Object, например, список массивов Integer может принимать только Integer s.)

ArrayList<? super Integer> a = new ArrayList<Number>();

Опять же, a является ссылкой на семейство типов (а не на один конкретный тип). Поскольку подстановочный знак использует super, эта ссылка на список может принимать Integer s, но не может создать Integer s. С другой стороны, мы знаем, что любой член семейства типов, который может указывать на a, может принять Integer. Однако не каждый член этого семейства может создавать Integer s.

PECS - производитель extends, потребитель super - эта мнемоника помогает вам помнить, что использование extends означает, что общий тип может выдавать определенный тип (но не может его принять). Использование super означает, что общий тип может потреблять (принимать) определенный тип (но не может его создать).

ArrayList<ArrayList<?>> a

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

= new ArrayList<ArrayList<?>>(); // correct

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

ArrayList<?> a

Ссылка на любой список массивов (член семейства типов списков массивов).

= new ArrayList<?>()

ArrayList<?> относится к любому типу из семейства типов списков массивов, но вы можете создавать экземпляр определенного типа.


См. также Как добавить в List <? расширяет число > структуры данных?

Ответ 3

У вас странные ожидания. Если бы вы дали цепочку аргументов, которые привели вас к ним, мы могли бы заметить недостаток в них. Как бы то ни было, я могу дать лишь краткое руководство по дженерикам, надеясь коснуться тех моментов, которые вы, возможно, неправильно поняли.

ArrayList<? extends Object> - это ArrayList, параметр типа которого известен как Object или его подтип. (Да, расширение в ограничениях типа имеет значение, отличное от прямого подкласса). Поскольку только ссылочные типы могут быть параметрами типа, это фактически эквивалентно ArrayList<?>.

То есть вы можете поместить ArrayList<String> в переменную, объявленную с помощью ArrayList<?>. Поэтому a1.add(3) является ошибкой времени компиляции. a1 объявленный тип разрешает a1 быть ArrayList<String>, к которому нельзя добавить Integer.

Ясно, что ArrayList<?> не очень полезен, поскольку вы можете вставлять в него нуль. Возможно, поэтому Java Spec запрещает его:

Это ошибка времени компиляции, если какая-либо из аргументы типа, используемые в классе выражение создания экземпляра аргументы типа подстановки

ArrayList<ArrayList<?>> напротив, является функциональным типом данных. Вы можете добавить в него все виды ArrayLists и получить их. И поскольку ArrayList<?> содержит только, но не является подстановочным типом, это правило не применяется.

Ответ 4

Многое связано с полиморфизмом. Когда вы назначаете

X = new Y();

X может быть гораздо меньше "специфическим", чем Y, но не наоборот. X - это просто дескриптор, к которому вы обращаетесь Y, причем Y - это реальная инстанцированная вещь,

Здесь вы получаете ошибку, потому что Integer - это число, но Number не является целым.

ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error

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

В ваших примерах с добавлением int (small i) не является допустимым объектом или целым числом.

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

Это не хорошо, потому что вы фактически не можете создать список массивов, содержащий?. Вы можете объявить его как таковой, а затем проклять почти все, что может произойти в new ArrayList<Whatever>();

Ответ 5

Подумайте о ? как о значении "unknown". Таким образом, "ArrayList<? extends Object>" означает "неизвестный тип, который (или до тех пор, пока он) расширяет Object". Поэтому нужно сказать, что arrayList.add(3) будет помещать то, что вы знаете, в неизвестное. I.e "Забыть".

Ответ 6

ArrayList<Integer> a = new ArrayList<Number>(); 

Не работает, потому что тот факт, что Number является супер-классом Integer, не означает, что List<Number> является супер-классом List<Integer>. Дженерики удаляются во время компиляции и не существуют во время выполнения, поэтому отношения между родителями и дочерними элементами не могут быть реализованы: информация о типе элемента просто удаляется.

ArrayList<? extends Object> a1 = new ArrayList<Object>();
a1.add(3);

Я не могу объяснить, почему это не работает. Это действительно странно, но это факт. Действительно синтаксис <? extends Object> в основном используется для возвращаемых значений методов. Даже в этом примере действителен Object o = a1.get(0).

ArrayList<?> a = new ArrayList<?>()

Это не работает, потому что вы не можете создать список неизвестного типа...