The toArray
method (lets pick the implementation in java.util.ArrayList
) is the following:
class ArrayList<E> ....{
public <T> T[] toArray(T[] a){
if(a.length < size)
return (T[]) Arrays.copyof(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if(a.length > size)
a[size] = null;
return a;
}
}
I am wondering could we use <E>
instead of <T>
in this case ? like
public E[] toArray(E[] a){
if(a.length < size)
return (E[]) Arrays.copyof(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if(a.length > size)
a[size] = null;
return a;
}
Since the ArrayList class iteself is already generic to <E>
, so could we use that instead of a new generic type <T>
?
I think John B's answer covered the idea well - I'd just like to elaborate on it a little.
First, let's look at the method signature you proposed in your question:
public E[] toArray(E[] a)
As John explained, this signature is less flexible. If I want to dump an ArrayList<Integer>
into a Number[]
, this method doesn't let me. The Collections API wants to allow that flexibility.
Unfortunately the method as it stands doesn't allow for compile-time checking on the type of the array, which is the issue you seem to be getting at. The documentation for toArray
, which is declared by the Collection
, explains that an ArrayStoreException
may be thrown "if the runtime type of the specified array is not a supertype of the runtime type of every element in this collection".
Based on that description, it seems like the following signature would be ideal:
public <T super E> T[] toArray(T[] a)
At first glance, this seems like it would allow any legal type of array to be passed in and populated - but would provide type checking at compile time instead of run time. So why is this signature not declared instead?
Well, this syntax:
<T super E>
is not supported by the language. But it's easy for that to distract you from the fact that this type checking wouldn't work anyway. The problem is that unlike parameterized types, arrays are covariant. An Integer[]
is a Number[]
is an Object[]
. So given our hypothetical signature with <T super E>
, if I called toArray
on an ArrayList<Number>
and passed in an Integer[]
, it would still compile - and possibly fail at runtime depending on what was in the list.
So the bottom line is, a lower bound on the component type of the array passed in isn't going to do any good.
The point of the <T>
is if the array desired is of a base class of E
. For example if E
is HashMap
but the desired array was Map[]
. If toArray
were locked down to E
this would not be possible.
This type of thing is not needed in generic collections / types due to type-erasure. But there is no type-erasure with arrays so the type of the array can be very important.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With