Why is this code valid
ArrayList<?>[] arr = new ArrayList<?>[2];
but the following two are not?
ArrayList<? extends Object>[] arr = new ArrayList<? extends Object>[2]; ArrayList<? super Object>[] arr = new ArrayList<? super Object>[2];
The two last rows generate the compile error;
error: generic array creation.
Please clarify difference.
update
On the other hand ArrayList<?>[] arr = new ArrayList<?>[2];
compiles good but
ArrayList<?> arr = new ArrayList<?>();
not.
There are a few issues going on here, lets look at each in turn:
A type bound (ie extends Object
) can only be declared when declaring a type, it cannot be used when instantiating an object.
for example
ArrayList<? extends Object> ab = new ArrayList<? extends Object>(); // error
ArrayList<? extends Object> ac = new ArrayList<String>(); // okay
Arrays do not support type parameters, for example:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile time error
List<Integer> list = new List<Integer>(); // okay
Oracle documents the reasons for this limitation here.
<?>
can be used when declaring a type parameter, and with arrays. It was added to help avoid 'unchecked exception' errors when mixing Java code that does and does not use generics. It means 'unknown generic type'. More detail on unbounded wild cards here.
ArrayList<?>[] arr = new ArrayList<?>[2];
is valid for the reasons outlined above. However it is of very limited use because only null can be assigned to types declared as <?>
.
arr[0] = null; // compiles
arr[1] = new Object(); // compile time error
Oracle provides the following Guide on using wildcards which will help to understand when to use this wild cards.
<?>
cannot be used to instantiate an object. For example
ArrayList<?> arr = new ArrayList<?>(); // does not compile
ArrayList<?> arr2 = new ArrayList<>(); // but this does
ArrayList<?> arr3 = new ArrayList<String>(); // and so does this
However one still has the problem that using <?>
only accepts null.
arr3.add( " " ); // does not compile even though it was instantiated with String
arr3.add( null ); // compiles just fine
You have to first understand why creating an array of a parameterized type is not allowed. It's because arrays check at runtime that elements inserted are instances of the component type (a la instanceof
). It is not possible to check instanceof
a parameterized type, because an object does not have a sense of the type parameter with which it was created. instanceof ArrayList<Integer>
is illegal in Java, as is something like instanceof ArrayList<? extends Number>
, but instanceof ArrayList<?>
is allowed in Java because it needs no information about the type parameter of the object. (By the way instanceof ArrayList<? extends Object>
and instanceof ArrayList<? super Object>
are also illegal.)
Conceptually, ArrayList<? extends Object>
is almost completely identical to ArrayList<?>
(there are minor differences but not relevant), but for consistency of the grammar, new ArrayList<? extends X>[...]
is not allowed for any X.