Casting Object Array to Integer Array Error

casting Object array to Integer array error

Ross, you can use Arrays.copyof() or Arrays.copyOfRange() too.

Integer[] integerArray = Arrays.copyOf(a, a.length, Integer[].class);
Integer[] integerArray = Arrays.copyOfRange(a, 0, a.length, Integer[].class);

Here the reason to hitting an ClassCastException is you can't treat an array of Integer as an array of Object. Integer[] is a subtype of Object[] but Object[] is not a Integer[].

And the following also will not give an ClassCastException.

Object[] a = new Integer[1];
Integer b=1;
a[0]=b;
Integer[] c = (Integer[]) a;

How can I cast an Object array to an array of arrays of integers

You can't change the intrinsic type of a Java object by casting it. That includes arrays. The object's type is fixed when the object is allocated, and cannot be changed.

So casting an Object[] to int[][] will never work.

In this case, while int[][] is a subtype of Object[], this is not sufficient for the typecast to be allowed at runtime. The actual rule in the JLS (5.5.3. Checked Casts at Run Time) is this:

"Here is the algorithm to check whether the run-time type R of an object is assignment compatible with the type T which is the erasure (§4.6) of the type named in the cast operator. If a run-time exception is thrown, it is a ClassCastException." ...

"If R is a class representing an array type RC[], that is, an array of components of type RC:"

  • "If T is an array type TC[], that is, an array of components of type TC, then a run-time exception is thrown unless one of the following is true:"

    • "TC and RC are the same primitive type."

    • "TC and RC are reference types and type RC can be cast to TC by a recursive application of these run-time rules for casting."

In this case TC is int[] and RC is Object and Object cannot be cast to int[]. Hence you get an exception.

To illustrate why this must happen, consider this (hypothetical) example:

   Object[] foo = new Object[3];
foo[0] = new Object();
int[][] bar = /* foo */
int[] mystery = bar[0];

Assuming that it is possible to "cast" the value of foo, what should the mystery variable point to? It can't be an int[] because we didn't allocate one. It can't be an Object instance because that would break static typing.

In fact, the "cast" has to be illegal, or else Java's static type system falls apart.



The difference between your example and mine is that foo is not actually an array of arrays of integers, whereas in my case it is. Why can the VM not see the "actual" type of the object?

The point is that static typing is (primarily) enforced by the compiler not by the JVM. While the JVM could (in theory) figure out that the types are OK at runtime (and throw exceptions if they are not), the compiler cannot do this because it cannot determine in general what the types are going to be.



Unfortunately I'm not actually creating the array myself. I'm getting it from another function and the Object[] rather than int[][] is, as far as I can tell, the side effect of some serialization that I have no control over.

Unfortunately you have to either copy the array ... or use it as an Object[] and cast the elements to int[] to use them; e.g.

Object[] foo = new Object[3];
foo[0] = new int[] { 1, 2, 3 };
foo[1] = new int[] { 4, 5, 6 };
foo[2] = new int[] { 7, 8, 9 };
...
int nine = ((int[]) (foo[2]))[2];

There is no "trust me it is really an int[][]" option in Java.

If this a side-effect of serialization then the serialization is broken ... in some sense. Certainly, this won't happen with the standard Java Object Serialization protocol / implementation. That preserves the types of the objects ... unless you explicitly override them in the readObject / writeObject methods, etcetera.

Java Generics: Array Casting

The reason the cast does not work is that type erasure of Node<E> applies to E inside Node, but it does not apply to arrays of Node<E>.

Type erasure would let you cast Node<E> to Node<Object> because all Node<E>s are Node<Object> under the hood. Similarly, it would let you cast Node<E>[] to Node<Object>[] and back, because the arrays share the same underlying type - that is, Node<Object>.

However, type erasure is not in play when you deal with arrays. An array of objects remains an array of objects, and Java has enough metadata to know it.

You have several ways of solving this problem:

  • Switch from Node<E>[] to ArrayList<Node<E>> - array list has similar footprint and access times, and is generic
  • Use a solution from this Q&A - type-safe solution requires passing Class<E>.

casting integer array to object array in java

You can't do that in the java. long is a primitive type, and does because of that not extend Object. Long, which is a wrapper class for long, does and can be cast to an Object. To create a Long[] from a long[] you will need to go through every value of long[] and copy that to Long[]:

long[] primitiveLong;
Long[] wrappedLong = new Long[primitiveLong.length];
for (int i=0; i<primitiveLong.length; i++) {
wrappedLong[i] = primitiveLong[i];
}

Then you can cast it to an array of Object:

Object[] objs = wrappedLong;

Or you can even make the wrappedLong of type Object directly so you don't need the casting.

Why is it wrong to cast Object[] array?

Part of the point is to look at it the other way around. You cannot do (String[]) new Object[10]; because an Object array is not a String array. Because

String[] array = new String[10];
array[0] = "foo";
String foo = array[0];

is fine, but

Object[] objectArray = new Object[10];
objectArray[0] = 10;
String[] stringArray = (String[]) objectArray;
String foo = stringArray[0];

...is trying to assign an Integer to a String, which shouldn't be allowed in the first place. So this code fails when you cast the Object[] to a String[]. That code has to throw a ClassCastException somewhere.

This is all the same for Java even before generics were invented in the first place. Accept all that first. Then move on to generics.

Now, the way Java generics are implemented means that when you compile the code, T is silently rewritten to Object. So T[] array = (T[]) new Object[10] is silently allowed, because it actually gets rewritten to Object[] array = new Object[10]. But as soon as you take it out, things go wrong. For example,

private static <T> T[] newArray() {
return (T[]) new Object[10];
}

if you call String[] array = newArray(), you'll get a ClassCastException at the call site, not within newArray(). This is why Java gives you a warning at (T[]) new Object[10], and that warning may well lead to a real ClassCastException later on.

Generally speaking, don't mix arrays and generics. The way around all this is to use a List properly.

Why can't I cast an array of Objects to an array of a generic subclass of a generic class?

All versions of your Foo class have the same problem. The Type safety: Unchecked cast from Object[] to X warning that you're ignoring just shows its consequences at different stages during execution.

Here's a way to break your first version:

class Foo<T> {
T[] items = (T[]) new Object[10];

public static void main(String[] args) {
Foo<Integer> foo = new Foo<Integer>();

Integer i = foo.items[1] * 2;
}
}

It crashes with... the same problem:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
at stackoverflow.Foo.main(Main.java:17)

Why? Because (T[]) new Object[10]; is not enforced at compile time, i.e., the compiler does not know how to validate the relationship between T[] and Object[]

Just as you can't cast new Object() to Integer, you can't cast new Object[10] to Integer[]. It's just that this class cast exception only shows up when the actual casting is run. For the first snippet, it does not run on the declaration statement, T[] items = (T[]) new Object[10]; (hence the warning), but when items is used as whatever the type argument is for T.

Your other versions show the problem sooner simply because they're casting to a concrete type, such as (FooItem[]) new Object[10], causing the declaration statement itself to crash.

Why does the compiler allow this cast? Because the type relationship is valid, given that such code as

Object[] items = new Integer[10];
Integer[] i = (Integer[])items;
Integer val = i[0];

is valid and supported.



Related Topics



Leave a reply



Submit