Actually, the question should be
Creating an array of generic anything.
Why can\'t the compiler take care of it?
The following would be
Compilers can spontaneously perform the conversion, they are just specified not to because generic arrays can't behave like non-generic arrays.
See 10.5. Array Store Exception:
For an array whose type is
A[]
, whereA
is a reference type, an assignment to a component of the array is checked at run time to ensure that the value being assigned is assignable to the component.If the type of the value being assigned is not assignment-compatible with the component type, an
ArrayStoreException
is thrown.If the component type of an array were not reifiable, the Java Virtual Machine could not perform the store check described in the preceding paragraph. This is why an array creation expression with a non-reifiable element type is forbidden.
A List
would not throw if we put some other kind of List
in it, so it doesn't behave as an array. Note the last sentence from the quote: "This is why an array creation expression with a non-reifiable element type is forbidden." This is the reason, it's specified to be so. (And, for the record, this reasoning has always existed, so it was present when the question was posted in 2011.)
We can still do this:
@SuppressWarnings({"unchecked","rawtypes"})
List[] dtoLists = new List[] {
new ArrayList(), anExistingDtoList
};
Or this:
@SuppressWarnings("unchecked")
List[] dtoLists = (List[]) new List>[] {
new ArrayList(), anExistingDtoList
};
(Besides statically checking the argument types, the varargs thing is equivalent: it creates a List[] and suppresses warnings.)
Now, sure, the specification could be changed to something like "If the type of the value being assigned is not assignment-compatible with the raw type of the component type...", but what is the point? It would save a handful of characters in some unusual situations but otherwise suppress warnings for those who don't understand the implications.
Furthermore, what the tutorial and other typical explanations I've seen don't demonstrate is just how baked in to the type system covariant arrays are.
For example, given the following declaration:
// (declaring our own because Arrays.fill is defined as
// void fill(Object[], Object)
// so the next examples would more obviously pass)
static void fill(T[] arr, T elem) {
Arrays.fill(arr, elem);
}
Did you know that this compiles?
// throws ArrayStoreException
fill(new String[1], new Integer(0));
And this compiles too:
// doesn't throw ArrayStoreException
fill(dtoLists, new ArrayList());
Before Java 8, we could make those calls to fill
fail by giving it the following declaration:
static void fill(T[] arr, U elem) {...}
But that was only a problem with type inference, and now it works "correctly", blindly putting List
in to a List
.
This is called heap pollution. It can cause a ClassCastException
to be thrown sometime later, likely somewhere completely unrelated to the actions that actually caused the problem. Heap pollution with a generic container like List
requires more obvious unsafe actions, like using raw types, but here, we can cause heap pollution implicitly and without any warnings.
Generic arrays (and really, arrays in general) only give us static checking in the simplest of circumstances.
So it's evident that the language designers thought it was better to just not allow them, and programmers who understand the problems they present can suppress warnings and bypass the restriction.