Steven
Steven

Reputation: 1809

How do I cast from an Object[] to another array type?

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

Answers (5)

Affe
Affe

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

FThompson
FThompson

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

Has QUIT--Anony-Mousse
Has QUIT--Anony-Mousse

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

Kevin Bowersox
Kevin Bowersox

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

Bohemian
Bohemian

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

Related Questions