This question already has an answer here:
- How to create a generic array in Java? 29 answers
I don't understand the connection between generics and arrays.
I can create array reference with generic type:
private E[] elements; //GOOD
But can't create array object with generic type:
elements = new E[10]; //ERROR
But it works:
elements = (E[]) new Object[10]; //GOOD
checked :
or unchecked :
Here is the implementation of
LinkedList<T>#toArray(T[])
:In short, you could only create generic arrays through
Array.newInstance(Class, int)
whereint
is the size of the array.You should not mix-up arrays and generics. They don't go well together. There are differences in how arrays and generic types enforce the type check. We say that arrays are reified, but generics are not. As a result of this, you see these differences working with arrays and generics.
Arrays are covariant, Generics are not:
What that means? You must be knowing by now that the following assignment is valid:
Basically, an
Object[]
is a super type ofString[]
, becauseObject
is a super type ofString
. This is not true with generics. So, the following declaration is not valid, and won't compile:Reason being, generics are invariant.
Enforcing Type Check:
Generics were introduced in Java to enforce stronger type check at compile time. As such, generic types don't have any type information at runtime due to type erasure. So, a
List<String>
has a static type ofList<String>
but a dynamic type ofList
.However, arrays carry with them the runtime type information of the component type. At runtime, arrays use Array Store check to check whether you are inserting elements compatible with actual array type. So, the following code:
will compile fine, but will fail at runtime, as a result of ArrayStoreCheck. With generics, this is not possible, as the compiler will try to prevent the runtime exception by providing compile time check, by avoiding creation of reference like this, as shown above.
So, what's the issue with Generic Array Creation?
Creation of array whose component type is either a type parameter, a concrete parameterized type or a bounded wildcard parameterized type, is type-unsafe.
Consider the code as below:
Since the type of
T
is not known at runtime, the array created is actually anObject[]
. So the above method at runtime will look like:Now, suppose you call this method as:
Here's the problem. You have just assigned an
Object[]
to a reference ofInteger[]
. The above code will compile fine, but will fail at runtime.That is why generic array creation is forbidden.
Why typecasting
new Object[10]
toE[]
works?Now your last doubt, why the below code works:
The above code have the same implications as explained above. If you notice, the compiler would be giving you an Unchecked Cast Warning there, as you are typecasting to an array of unknown component type. That means, the cast may fail at runtime. For e.g, if you have that code in the above method:
and you call invoke it like this:
this will fail at runtime with a ClassCastException. So, no this way will not work always.
What about creating an array of type
List<String>[]
?The issue is the same. Due to type erasure, a
List<String>[]
is nothing but aList[]
. So, had the creation of such arrays allowed, let's see what could happen:Now the ArrayStoreCheck in the above case will succeed at runtime although that should have thrown an ArrayStoreException. That's because both
List<String>[]
andList<Integer>[]
are compiled toList[]
at runtime.So can we create array of unbounded wildcard parameterized types?
Yes. The reason being, a
List<?>
is a reifiable type. And that makes sense, as there is no type associated at all. So there is nothing to loose as a result of type erasure. So, it is perfectly type-safe to create an array of such type.Both the above case is fine, because
List<?>
is super type of all the instantiation of the generic typeList<E>
. So, it won't issue an ArrayStoreException at runtime. The case is same with raw types array. As raw types are also reifiable types, you can create an arrayList[]
.So, it goes like, you can only create an array of reifiable types, but not non-reifiable types. Note that, in all the above cases, declaration of array is fine, it's the creation of array with
new
operator, which gives issues. But, there is no point in declaring an array of those reference types, as they can't point to anything butnull
(Ignoring the unbounded types).Is there any workaround for
E[]
?Yes, you can create the array using
Array#newInstance()
method:Typecast is needed because that method returns an
Object
. But you can be sure that it's a safe cast. So, you can even use @SuppressWarnings on that variable.Problem is that while runtime generic type is erased so
new E[10]
would be equivalent tonew Object[10]
.This would be dangerous because it would be possible to put in array other data than of
E
type. That is why you need to explicitly say that type you want by eitherE[]
array, orcomponentType
argiment.