I was just looking at the method defined in the List interface: <T> T[] toArray(T[] a)
, and I have a question. Why is it generic? Because of that fact, method is not complete type-safe. The following code fragment compiles but causes ArrayStoreException
:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
String[] stringArray = list.toArray(new String[]{});
It seems to me if toArray was not generic and took List type parameter, it would be better.
I have written toy example and it is ok witout generic:
package test;
import java.util.Arrays;
public class TestGenerics<E> {
private Object[] elementData = new Object[10];
private int size = 0;
public void add(E e) {
elementData[size++] = e;
}
@SuppressWarnings("unchecked")
//I took this code from ArrayList but it is not generic
public E[] toArray(E[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (E[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public static void main(String[] args) {
TestGenerics<Integer> list = new TestGenerics<Integer>();
list.add(1);
list.add(2);
list.add(3);
//You don't have to do any casting
Integer[] n = new Integer[10];
n = list.toArray(n);
}
}
Is there any reason why it is declared that way?
From the javadocs:
Like the toArray() method, this method acts as bridge between array-based and collection-based APIs. Further, this method allows precise control over the runtime type of the output array, and may, under certain circumstances, be used to save allocation costs.
This means that the programmer is in control over what type of array it should be.
For example, for your ArrayList<Integer>
instead of an Integer[]
array you might want a Number[]
or Object[]
array.
Furthermore, the method also checks the array that is passed in. If you pass in an array that has enough space for all elements, the the toArray
method re-uses that array. This means:
Integer[] myArray = new Integer[myList.size()];
myList.toArray(myArray);
or
Integer[] myArray = myList.toArray(new Integer[myList.size()]);
has the same effect as
Integer[] myArray = myList.toArray(new Integer[0]);
Note, in older versions of Java the latter operation used reflection to check the array type and then dynamically construct an array of the right type. By passing in a correctly sized array in the first place, reflection did not have to be used to allocate a new array inside the toArray
method. That is no longer the case, and both versions can be used interchangeably.