Ключ кроется в различиях между ссылками и экземплярами и в том, что ссылка может обещать, и в чем действительно может быть экземпляр.
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<?>
относится к любому типу из семейства типов списков массивов, но вы можете создавать экземпляры только определенного типа.
См. Также Как я могу добавить в список <? расширяет число> структур данных?