Java generics in ArrayList.toArray()
The pertinent thing to note is that arrays in Java know their component type at runtime. String[]
and Integer[]
are different classes at runtime, and you can ask arrays for their component type at runtime. Therefore, a component type is needed at runtime (either by hard-coding a reifiable component type at compile time with new String[...]
, or using Array.newInstance()
and passing a class object) to create an array.
On the other hand, type arguments in generics do not exist at runtime. There is absolutely no difference at runtime between an ArrayList<String>
and a ArrayList<Integer>
. It is all just ArrayList
.
That's the fundamental reason why you can't just take a List<String>
and get a String[]
without passing in the component type separately somehow -- you would have to get component type information out of something that doesn't have component type information. Clearly, this is impossible.
If you look at the implementation of toArray(T[] a)
of ArrayList<E> class, it is like:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
Problem with this method is that you need to pass array of the same generic type. Now consider if this method do not take any argument then the implementation would be something similar to:
public <T> T[] toArray() {
T[] t = new T[size]; // compilation error
return Arrays.copyOf(elementData, size, t.getClass());
}
But the problem here is that you can not create generic arrays in Java because compiler does not know exactly what T
represents. In other words creation of array of a non-reifiable type (JLS §4.7) is not allowed in Java.
Another important quote from Array Store Exception (JLS §10.5):
If the component type of an array were not reifiable (§4.7), 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 (§15.10.1).
That is why Java has provided overloaded version toArray(T[] a)
.
I will override the toArray() method to tell it that it will return an array of E.
So instead of overriding toArray()
, you should use toArray(T[] a)
.
Cannot Create Instances of Type Parameters from Java Doc might also be interesting for you.
If you look at the Javadoc for the List
interface, you'll notice a second form of toArray
: <T> T[] toArray(T[] a)
.
In fact, the Javadoc even gives an example of how to do exactly what you want to do:
String[] y = x.toArray(new String[0]);
Generic information is erased at runtime. JVM does not know whether your list is List<String>
or List<Integer>
(at runtime T
in List<T>
is resolved as Object
), so the only possible array type is Object[]
.
You can use toArray(T[] array)
though - in this case JVM can use the class of a given array, you can see it in the ArrayList
implementation:
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());