Java generics - ArrayList initialization
It is known that arraylist init. should be like this
ArrayList<A> a = new ArrayList<A>();
ArrayList<Integer> a = new ArrayList<Number>(); // compile-time error
so, why does java allow these ?
1. ArrayList<? extends Object> a1 = new ArrayList<Object>();
2. ArrayList<?> a2 = new ArrayList<Integer>();
then, if they are correct why doesn't allow these ?
1. a1.add(3);
2. a2.add(3);
the compiler message is : The method add(int, capture#1-of ? extends Object) in the type ArrayList is not applicable for the arguments (int)
more general
1. a1.add(null e);
2. a2.add(? e);
I read about this but it is good to hear from you. thanks
the other amusing point is :
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
You can't assign a List<Number>
to a reference of type List<Integer>
because List<Number>
allows types of numbers other than Integer
. If you were allowed to do that, the following would be allowed:
List<Number> numbers = new ArrayList<Number>();
numbers.add(1.1); // add a double
List<Integer> ints = numbers;
Integer fail = ints.get(0); // ClassCastException!
The type List<Integer>
is making a guarantee that anything it contains will be an Integer
. That's why you're allowed to get an Integer
out of it without casting. As you can see, if the compiler allowed a List
of another type such as Number
to be assigned to a List<Integer>
that guarantee would be broken.
Assigning a List<Integer>
to a reference of a type such as List<?>
or List<? extends Number>
is legal because the ?
means "some unknown subtype of the given type" (where the type is Object
in the case of just ?
and Number
in the case of ? extends Number
).
Since ?
indicates that you do not know what specific type of object the List
will accept, it isn't legal to add anything but null
to it. You are, however, allowed to retrieve any object from it, which is the purpose of using a ? extends X
bounded wildcard type. Note that the opposite is true for a ? super X
bounded wildcard type... a List<? super Integer>
is "a list of some unknown type that is at least a supertype of Integer
". While you don't know exactly what type of List
it is (could be List<Integer>
, List<Number>
, List<Object>
) you do know for sure that whatever it is, an Integer
can be added to it.
Finally, new ArrayList<?>()
isn't legal because when you're creating an instance of a paramterized class like ArrayList
, you have to give a specific type parameter. You could really use whatever in your example (Object
, Foo
, it doesn't matter) since you'll never be able to add anything but null
to it since you're assigning it directly to an ArrayList<?>
reference.
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?