Reputation: 1809
I have a generic (I'm not too sure if that's the correct terminology..) method that adds an object to the an array.
@SuppressWarnings("unchecked")
public static <T>T[] appendArray(T[] a, T b)
{
T[] temp = (T[])new Object[a.length + 1];
System.arraycopy(a, 0, temp, 0, a.length);
temp[a.length] = b;
return temp;
}
In Eclipse, this code gives me no errors, no warnings (except for the suppressed "unchecked" warning), and, it seems to me, that this should work.. However, when I attempt to call this method with something such as..
Integer[] a=new Integer[]{1,2,3};
Integer b=4;
a = appendArray(a, b);
It gives me a ClassCastException
on, in this case, line 3 of the 2nd snippet of code I posted. The error says, [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
I neither see, nor understand why it would be doing this.. I mean, I'm using generics, so why would it there be an Object[]
other than the one which I cast to type T
during instantiation? And if it is the one at line number 4 (in the 1st bit of code, of course), why isn't the exception being thrown there rather than at line 3 of the 2nd bit of code?
Upvotes: 0
Views: 359
Reputation: 47994
Other answers have covered how to rewrite the code to work, but the second half of your question is still there, so:
You seem to misunderstand fundamentally what a cast is. It does not change the real type of anything. It tells the compiler "Hey, pretend this reference of type X is actually a Y, trust me, I know."
Integer i = (Integer) new Object();
That doesn't cause an Object
to become an Integer
! It throws an error at runtime when JVM figures out that you lied to the compiler.
The reason the error happens later than you expect in your example is that 'T' is a compile-time-only concept in java. For runtime purposes you can replace every 'T' with Object
.
As part of the Type Erasure process, code that checks to see if the claim you are making that this method returns an Integer[]
gets put in at the point where your method returns and tries to make an assignment.
Upvotes: 2
Reputation: 28707
Instead of using System.arraycopy
, use Arrays.copyOf
, which instantiates the array as the generic type.
public static <T> T[] appendArray(T[] a, T b) {
T[] temp = (T[]) Arrays.copyOf(a, a.length + 1, a.getClass());
temp[a.length] = b;
return temp;
}
Upvotes: 3
Reputation: 77485
You can't cast an array to a different array.
Arrays do have a value type. This is what [Ljava.lang.Integer;
says: an array of Integers. And that is an incompatible type to an array of objects.
Have you noticed that all the java collections classes use Object[]
? The reason is that this is the array type they are sure they can store their data in.
Also notice how the toArray
method looks like. It needs to know the type of array you want to get out. It cannot guess the type right from the generics; essentially you need a prototype object, and you then need to create an array of the proper type.
Upvotes: 2
Reputation: 94499
You could create an ArrayList
with generic a generic type of T
and get it as an array.
@SuppressWarnings("unchecked")
public static <T>T[] appendArray(T[] a, T b)
{
T[] temp = new ArrayList<T>(Arrays.asList(a)).toArray(a);
System.arraycopy(a, 0, temp, 0, a.length);
temp[a.length-1] = b;
return temp;
}
Upvotes: 0
Reputation: 425348
Generics and arrays don't mix!
There is a way around it. You can use the special Array.newInstance()
method to create an array object with the specific component type:
public static <T> T[] appendArray(T[] a, T b) {
T[] temp = (T[]) Array.newInstance(b.getClass(), a.length + 1);
System.arraycopy(a, 0, temp, 0, a.length);
temp[a.length] = b;
return temp;
}
This code does not throw any errors when called:
public static void main(String[] args) {
Integer[] a = new Integer[] { 1, 2, 3 };
Integer b = 4;
a = appendArray(a, b);
System.out.println(Arrays.toString(a));
}
Output:
[1, 2, 3, 4]
Upvotes: 1