I have below generic method that returns a generic array:
public static <T> T[] genericMethod1(List<T> input) {
T[] res = (T[]) new Object[input.size()];
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
But later when I try to get the result array with:
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);
Integer[] i = (Integer[]) genericMethod1(list); // 1) Runtime error
Integer j = genericMethod2(list); // 2) works
For case 1, I always get error at runtime:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Anybody can explain why and how to return the generic array properly? Thanks.
Below is my understanding, please correct me if I'm wrong.
As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".
Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.
EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.
Java allows generic classes, methods, etc. that can be declared independent of types. However, Java does not allow the array to be generic. The reason for this is that in Java, arrays contain information related to their components and this information is used to allocate memory at runtime.
Although we cannot instantiate a generic array of a specific type parameter, we can pass an already created array to a generic class constructor.
The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod()
will look like after the compiler performs type erasure:
public static Object[] genericMethod(List input) {
Object[] res = new Object[input.size()];
int i = 0;
for (Object t : input) {
res[i] = t;
i++;
}
return res;
}
In other words, this method will return an array of type Object
. There is no way to cast an Object[]
to an Integer[]
because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance()
. This will require also passing in the type of the array you want as an input parameter:
public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
@SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(clazz, input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
Now your code snippet will run without error:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] i = genericMethod(Integer.class, list);
Update:
Your second method, genericMethod2()
, will look like this after type erasure:
public static Object genericMethod2(List input) {
return input.get(0);
}
It will return the first element of the input list, cast to Object
. Here is your usage of that method:
Integer j = genericMethod2(list);
The compiler will try to cast the output from genericMethod2()
to Integer
:
Integer j = (Integer)genericMethod2(list);
This cast is legal, because every Integer
is also an Object
, and furthermore it succeeds here because you passed in a collection of Integer
. This second method is not the same scenario as the first one you highlighted for us.
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