Tuesday, April 12, 2011

Why is toArray() a generic method in Java Collections?

Probably some java enthusiast can answer this question for me. I was referring to the Collections API to convert a list to array. I was shocked to find that the method was generic.

<T> T[] toArray(T[] a)

I was wondering why the method was declared generic when the type is itself declared with one. So, in order to convince myself, I wrote the following code and a test case. Please excuse if the code looks odd and isn't following any standard; its written quickly for the purpose of this blog.




public class AList<E> {

private final List<E> list;

public AList(List<E> list) {
super();
this.list = list;
}

public boolean add(E value){
return this.list.add(value);
}

public int size(){
return this.list.size();
}

public E[] toArray(E[] a) {
System.arraycopy(list.toArray(), 0, a, 0, size());
return a;
}

}

The first part of the test case is for pre-Java5 collection and the next for the type safe Java 5 way.




@Test
public void testAListToArray() {
AList list = new AList(new ArrayList());
list.add("Not type safe");
String[] sl = (String[]) list.toArray(new String[list.size()]);
assertEquals(sl.length, list.size());

AList<String> list2 = new AList<String>(new ArrayList<String>());
list2.add("Type safe");
String[] s2 = list2.toArray(new String[list.size()]);
assertEquals(s2.length, list2.size());
}

Both worked perfectly fine and the test passed. So, back to square one --> I'm unable to figure out why they method is declared generic with an additional parameterized type.

1 comment:

Leo D said...

Hello! I hope that this isn't too late a reply.

I was actually doing some research on this myself. And here is my explanation.

The code may work at the specifics of a List, so I will go up to a generic collection that is based by an Array of type Object[].

This really has to do with the component type of the Array.

In order to create a "generic array", you have to use java.lang.reflect.Array.newInstance(Class, int). This method takes in a class object which determines the component type of the new array.

The only other way to make a "generic array", would be to typecast an Object array with the right class. For example, (E[])new Object[x].

The disadvantage of doing this is that the component type is actually "Object" rather than "E". So if you used this to create an Array of Strings, you'll get Object as the component type.

Now, there are only really two ways to fix this: store the component type of the collection, or find some way to put a component type into the method that creates the generic array.

The first one makes sense if the collection were type-checking, because you already have a component type for it. But Java opted for the second one because it's more flexible* and avoids having to store the component type.

*You can use this to create a more generic type of Array. For example, if you have a collection of Integers, you can get an array of numbers from it. Or you can use it if you know that you only have a specific type of element in your collection. For example, getting an array of Gregorian calendars from a collection of any calendars.

I hope that this helps! :)

Just remember, even if you type cast, Object[], you still have an array of objects, but if you use Array.newInstance that is actually a generic array.