Creating an array from a generic Java collection
Whilst working on Jaggregate, I ran into some surprises involving Java generics (shocking, no?). Today's subject: turning a generic collection into an array.
The uppermost interface in Jaggregate's type hierarchy is Collection<E>. We might wish to ask such a collection for an array of its elements. If we don't really want to do anything with specific elements, we can simply define a toArray() method which answers an object of type Object[]:
interface Collection<E> {
Object[] toArray();
}
If you do want to manipulate the elements, you really want something like:
interface Collection<E> {
E[] toArray();
}
This method would need to create an array of the appropriate component type, populate the array, and return it. However, at runtime, the information about the type of E is no longer around; it gets discarded (erased) during compilation. Thus an invocation such as:
Collection<String> names;
String[] namesAsArray = names.toArray();
would raise a ClassCastException -- the best the runtime can do is create an Object[], which is-not-a String[], even if you try to cast the created array to E[] internally.
Fortunately, we can get around this problem by making the caller tell us what she wants the component type of the result to be:
interface Collection<E> {
Object[] toArray( Class<? super E> componentType );
}
Now, the client can hand us a Class object that represents E or any one of its ancestors, and the runtime can create an array of the appropriate component type via java.lang.reflect.Array.newInstance(componentType,size()). But wait, the return type of toArray() is still Object[]. The client has to downcast to, say, String[], but at least it will be a safe cast.
Can we do better? We want the return type to be some array type where the array's component type is a specified ancestor of E. Maybe making toArray() a generic method will help?
interface Collection<E> {
<T> T[] toArray( Class<T super E> componentType );
}
Doesn't compile.
interface Collection<E> {
<T,E extends T> T[] toArray( Class<T> componentType );
}
Doesn't compile either.
interface Collection<E> {
<T super E> T[] toArray( Class<T> componentType );
}
Nope. Bummer!
I resorted to a utility method:
class Collections {
public static <T,E extends T> T[] toArray(
final Collection<E> collection,
final Class<T> componentType ) {
}
}
Does anyone have a better idea? Note that I'm not interested in having the client create the array to populate and pass it to toArray(), as java.util.Collection requires.