Let’s implement a generic factory method for arrays in Java like this:
static <T> T[] createArray(T... t) { return t; }
We can use this method to create any array. For example an array of strings:
String[] strings = createArray("some", "thing");
Now let’s add another twist:
static <T> T[] crash(T t) { return createArray(t); } String[] outch = crash("crash", "me");
Running this code will result in a ClassCastException on the last line:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
At first this seems strange. There is no cast anywhere here. So what is going on? Basically the Java compiler is lying at us: calling the crash method with string arguments, it tells us that we get back an array of strings. Now looking at the exception we see that this is not true. What we really get back is an array of objects!
Actually the Java compiler issues a warning on the createArray call in the crash method:
Type safety : A generic array of T is created for a varargs parameter
This is how it tells us about its lying: “Since I don’t know the actual type of T, I’ll just return an array of Object instead.” I thinks this is wrong. And others seem to think along the same lines.
[…] This post was mentioned on Twitter by Planet Scala, Planet Lang. Planet Lang said: [Scala] Generic array factory in Java: receipt for disaster: Let’s implement a generic factory method for arrays… http://bit.ly/cszx6k […]
Java arrays are covariant, so this should not be a surprise, *particularly* not when the compiler even warns you.
Now, we can agree that covariant arrays are bad, but that’s been the state of Java since 1.0
This does not change my point: the compiler lies. At least it warns about its untruthfulness .
I understand that the issue at hand is an unfortunate interplay between covariant arrays, generic types and implementing varargs through arrays. But making the compiler lie seem plain wrong to me. That code should not compile at all!
In Java, object arrays are currently covariant. Actually they are, but only when reading. To solve the problem, the ‘read’ and the ‘write’ type would need to be specified, so instead of (Object[] t) it would be (Object/String[] t) which means read objects, but store strings. This doesn’t solve the problem above however.
https://stackoverflow.com/questions/529085/how-to-generic-array-creation/18137953#18137953
this shows a better approach for creating generic arrays in Java. don’t use varargs as it can be fickle. Use the Class:
import java.lang.reflect.Array;
…
public static E[ ] genericArray( Class elementType, int capacity ) {
E[ ] gArray = null;
if( elementType != null && !elementType.isPrimitive() ) {
if( capacity < 0 ) {
capacity = 0;
}
gArray = (E[ ])Array.newInstance( elementType, capacity );
}
return gArray;
}
this works for everything except primitive arrays. Worst case it returns null, but it won't cause any run time exceptions.
^the generic parameter int the first line for E got cut off
*after the static keyword should have the [greater than] E [less than]
*same with after Class
public static E[ ] genericArray( Class elementType, int capacity ) {