9

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.

gstackoverflow
  • 36,709
  • 117
  • 359
  • 710

2 Answers2

13

There are a few issues going on here, lets look at each in turn:

  1. 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

  2. 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.

  3. <?> 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.

  4. <?> 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

Chris K
  • 11,622
  • 1
  • 36
  • 49
  • +1 - I hadn't considered the interaction with arrays. Only the type declaration itself. – Rudi Kershaw Oct 01 '14 at 12:11
  • *Perhaps the first question should be, why does the following not compile:* - no, I know it – gstackoverflow Oct 01 '14 at 14:06
  • @gstackoverflow I'm sorry, I did not follow you. What did you mean? – Chris K Oct 01 '14 at 14:07
  • @gstackoverflow ping me when you have had a chance to absorb, I can work in any clarifications. The key point though is this, 'Java does not support generic arrays'. Period. Having `ArrayList>[]` work was in some ways a red herring, and it exists as a special case to integrate code that uses generics with code that does not. – Chris K Oct 01 '14 at 14:18
  • The following is an excellent tutorial: http://docs.oracle.com/javase/tutorial/java/generics/index.html – Chris K Oct 01 '14 at 14:22
  • @Chris K **ArrayList> arr = new ArrayList>;** it doesn't compile without arrays brackests. – gstackoverflow Oct 01 '14 at 16:47
  • @gstackoverflow please recheck, I think that I got caught out by stackoverflows auto formatting. I have added quotes to stop it formatting out the java syntax. – Chris K Oct 01 '14 at 16:52
  • @Chris K did not understand the causal relationship after word **Thus** – gstackoverflow Oct 01 '14 at 17:09
  • @gstackoverflow I have removed the word 'thus', and made a sentence tweak to make it clearer. – Chris K Oct 01 '14 at 17:19
  • @Chris K my current opinion: in general unbounded wild card doesn't limit type thus array shouldn't check type before element adding for example to the array thus we cannot get *ArrayStoredException*. But bounded wild card limit type thus because we use array we should to know this type in runtime but it is impossible for generics. – gstackoverflow Oct 02 '14 at 10:54
  • @Chris K What do u think about this? – gstackoverflow Oct 02 '14 at 10:57
  • @gstackoverflow I think that you are pretty much there. However an unbounded wild card does not mean 'doesn't limit type', it means 'unknown type'. There is a difference. As for arrays should know type at runtime, yes a language can be designed to know. However as I think you said by 'impossible for generics', Java does not support that behavior. In general Java Generics are a big bag of trade offs, mostly pragmatic ones. – Chris K Oct 02 '14 at 12:37
3

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.

newacct
  • 119,665
  • 29
  • 163
  • 224