Error about creating array of Generic List in Java

Though I don't have time to dig very deep in the JLS, I can hint you were to look further (though every time I do this, it ain't a very pleasant trip).

List<Integer>[] array = (List<Integer>[]) new Object[size]; 

this does not compile because these are provably distinct types (search the JLS for such a notion). In simpler words, the compiler is "able" to see these types can not possibly be of same type that can potentially be casted, thus fails.

On the other hand:

array = (E[]) new Object[10];

these are not a provably distinct types; the compiler can't tell for a fact that this has to fail. The slightly other thing here, is that casting to a generic type is not enforced by the compiler in no form or shape, you could have easily do something like this (that would still compile):

String s[][][] = new String[1][2][3];
array = (E[]) s; // this will compile, but makes little sense 

The second point is type erasure (again JLS has it).

After you compile the code, E[], at runtime, is Object[] (unless there is a bound, but not the case here), well you can obviously put anything you want into that.


First Code

List<Integer>[] array = (List<Integer>[]) new Object[size]; 

The reason why the first code fails is because casting does not change the actual type of the array, it just makes the compiler accept the code as valid. Imagine if you had another reference to the underlying object array:

final int size = 2;
Object[] objectArr = new Object[size];
List<Integer>[] integerArr = (List<Integer>[]) objectArr; // Does not work
objectArr[0] = "foobar";
List<Integer> i = integerArr[0]; // What would happen ??

The above code compiles fine, since you are forcing the compiler to accept it with the cast. But you can already see why it would be a problem for the cast to work at runtime: you would end up with an List<Integer>[] that now contains a String, which makes no sense. So the language disallows this.

Second Code

E[] array = (E[]) new Object[size];

Generics in Java are kind of odd. For various reasons, such as backwards compatibility, generics are basically erased by the compiler and will (mostly) not appear in the compiled code (Type Erasure). Instead, it will use a series of rules (JLS spec) to determine what type should be used in the code instead. For a basic unbouded generic; this type will be Object. So, assuming there is no bound on E, the second code is changed by the compiler to this:

 Object[] array = (Object[]) new Object[size];

So since both arrays have the exact same type after the erasure, there is no problem at runtime and the cast is basically redundant.

It is worth noting that this only works as long as E is unbounded. For example, this will fail at runtime with a ClassCastException:

public static <E extends Number> void genericMethod() {
    final int size = 5;
    E[] e = (E[]) new Object[size];
}

That is because E will be erased to Number, and you will get the same problem as the first code:

Number[] e = (Number[]) new Object[size];

It is important to keep the erasure in mind when working with code. Otherwise you may run into situations where the code acts different from what you expect. For example, the following code compiles and runs without exceptions:

public static <E> void genericMethod(E e) {
    final int size = 2;
    Object[] objectArr = new Object[size];
    objectArr[0] = "foobar";

    @SuppressWarnings("unchecked")
    E[] integerArr = (E[]) objectArr;
    integerArr[1] = e;

    System.out.println(Arrays.toString(integerArr));
    System.out.println(e.getClass().getName());
    System.out.println(integerArr.getClass().getName());
}

public static void main(String[] args) {
    genericMethod(new Integer(5)); // E is Integer in this case
}

Third Code

List<Integer>[] array = (List<Integer>[]) new ArrayList[size];

Similarly to the case above, the third code will be erased to the following:

 List[] array = (List[]) new ArrayList[size];

Which is no problem because ArrayList is a subtype of List.

Fourth Code

List<Integer>[] array = new ArrayList<Integer>[size];

The above will not compile. The creation of arrays with a type that has a generic type parameter is explicitely disallowed by the spec:

It is a compile-time error if the component type of the array being initialized is not reifiable (§4.7).

A type with a generic parameter that is not an unbounded wildcard (?) does not satisfy any condition for reifiability:

A type is reifiable if and only if one of the following holds:

  • It refers to a non-generic class or interface type declaration.
  • It is a parameterized type in which all type arguments are unbounded wildcards (§4.5.1).
  • It is a raw type (§4.8).
  • It is a primitive type (§4.2).
  • It is an array type (§10.1) whose element type is reifiable.
  • It is a nested type where, for each type T separated by a ".", T itself is reifiable.