简体   繁体   中英

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

This is a bit of a contrived example to get across the situation I'm encountering, but basically, I have an array of Objects that in reality contains arrays of integers, and I'm trying to cast it as such. Here is a code snippet that simulates this situation:

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[][] bar = (int[][]) foo;

When I try to do this, I get the exception Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[I Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[I

Is there a way to do this without copying the array (which I may not want to do since foo will be very large in production)? If not, why not?

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; eg

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.

Since foo is array of objects it means it can contain anything, not only array of integers so compiler cant allow you to cast it to array of integers only because you know that it is inside.

If you want to use int[][] bar reference you will have to create separate int [3][] array first and copy content of foo to that array.

You need to cast each object as you access it.

    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[] foo2 = (int[])foo[2];
    System.out.println("foo[2,2]="+((int[])foo[2])[2]);

You should also wrap this in try ... catch , if you think the contents could be non-ints.

Edit: simplify code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM