Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

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

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?

like image 838
Catherine Avatar asked Sep 17 '13 01:09

Catherine


People also ask

How do I cast an object to an array?

To convert an object into an array in Javascript, you can use different types of methods. Some of the methods are Object. keys(), Object. values(),and Object.

Can you cast an object array?

you try to cast an Array of Object to cast into Array of Integer. You cant do it. This type of downcast is not permitted. You can make an array of Integer, and after that copy every value of the first array into second array.

Can you cast an object to an int?

You can cast an Object to an Integer and then assign it to an int and it will magically autounbox.


2 Answers

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.

like image 157
Stephen C Avatar answered Oct 14 '22 22:10

Stephen C


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.

like image 38
andy256 Avatar answered Oct 14 '22 22:10

andy256