Обобщения Java - инициализация ArrayList - PullRequest
20 голосов
/ 20 декабря 2010

Известно, что 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's reason but I have some 
question in my mind that mentioned above 

Ответы [ 6 ]

13 голосов
/ 20 декабря 2010

Нельзя присвоить 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<Integer> List другого типа, например Number, эта гарантия была бы нарушена.

Назначение 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<?> ссылка.

8 голосов
/ 20 декабря 2010

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

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

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

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

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

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

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

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

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

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

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

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

ArrayList<ArrayList<?>> a

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

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

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

ArrayList<?> a

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

= new ArrayList<?>()

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


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

2 голосов
/ 20 декабря 2010

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

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

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

Очевидно, что ArrayList<?> не очень полезен, так как вы можете вставить в него только нуль. Возможно, поэтому спецификация Java запрещает it:

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

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

1 голос
/ 20 декабря 2010

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

X = new Y();

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

Вы получаете ошибку здесь, потому что Целое число - это Число, а Число - это не Целое число.

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

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

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

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

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

0 голосов
/ 13 апреля 2016

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

0 голосов
/ 20 декабря 2010
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<?>()

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

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