The key lies in the differences between references and instances and what the reference can promise and what the instance can really do.
ArrayList<A> a = new ArrayList<A>();
Here a
is a reference to an instance of a specific type - exactly an array list of A
s. More explicitly, a
is a reference to an array list that will accept A
s and will produce A
s. new ArrayList<A>()
is an instance of an array list of A
s, that is, an array list that will accept A
s and will produce A
s.
ArrayList<Integer> a = new ArrayList<Number>();
Here, a
is a reference to exactly an array list of Integers
, i.e. exactly an array list that can accept Integer
s and will produce Integer
s. It cannot point to an array list of Number
s. That array list of Number
s can not meet all the promises of ArrayList<Integer> a
(i.e. an array list of Number
s may produce objects that are not Integer
s, even though its empty right then).
ArrayList<Number> a = new ArrayList<Integer>();
Here, declaration of a
says that a
will refer to exactly an array list of Number
s, that is, exactly an array list that will accept Number
s and will produce Number
s. It cannot point to an array list of Integer
s, because the type declaration of a
says that a
can accept any Number
, but that array list of Integer
s cannot accept just any Number
, it can only accept Integer
s.
ArrayList<? extends Object> a= new ArrayList<Object>();
Here a
is a (generic) reference to a family of types rather than a reference to a specific type. It can point to any list that is member of that family. However, the trade-off for this nice flexible reference is that they cannot promise all of the functionality that it could if it were a type-specific reference (e.g. non-generic). In this case, a
is a reference to an array list that will produce Object
s. But, unlike a type-specific list reference, this a
reference cannot accept any Object
. (i.e. not every member of the family of types that a
can point to can accept any Object
, e.g. an array list of Integer
s can only accept Integer
s.)
ArrayList<? super Integer> a = new ArrayList<Number>();
Again, a
is a reference to a family of types (rather than a single specific type). Since the wildcard uses super
, this list reference can accept Integer
s, but it cannot produce Integer
s. Said another way, we know that any and every member of the family of types that a
can point to can accept an Integer
. However, not every member of that family can produce Integer
s.
PECS - Producer extends
, Consumer super
- This mnemonic helps you remember that using extends
means the generic type can produce the specific type (but cannot accept it). Using super
means the generic type can consume (accept) the specific type (but cannot produce it).
ArrayList<ArrayList<?>> a
An array list that holds references to any list that is a member of a family of array lists types.
= new ArrayList<ArrayList<?>>(); // correct
An instance of an array list that holds references to any list that is a member of a family of array lists types.
ArrayList<?> a
An reference to any array list (a member of the family of array list types).
= new ArrayList<?>()
ArrayList<?>
refers to any type from a family of array list types, but you can only instantiate a specific type.
See also How can I add to List<? extends Number> data structures?
a1.add(new Integer(3));
? – Anomalistic