8

这是一个人为的例子来解决我遇到的情况,但基本上,我有一个对象数组,实际上包含整数数组,我正试图将它转换为这样。这是一个模拟这种情况的代码片段:

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;

当我尝试这样做时,我得到了异常Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[I

有没有办法在不复制数组的情况下做到这一点(我可能不想这样做,因为foo在生产中会非常大)?如果不是,为什么不呢?

4

3 回答 3

11

您不能通过强制转换来更改 Java 对象的内在类型。这包括数组。对象的类型在分配对象时是固定的,不能更改。

所以铸造一个Object[]永远int[][]不会奏效。

在这种情况下,whileint[][]是 的子类型Object[],这不足以在运行时允许类型转换。JLS 中的实际规则(5.5.3. Checked Casts at Run Time)是这样的:

“这是检查对象的运行时类型 R 是否与类型 T 的赋值兼容的算法,类型 T 是强制转换运算符中命名的类型的擦除(第 4.6 节)。如果抛出运行时异常,它是一个ClassCastException。” ...

“如果 R 是表示数组类型 RC[] 的类,即 RC 类型的组件数组:”

  • “如果 T 是 TC[] 类型的数组,即 TC 类型的组件数组,则除非满足以下条件之一,否则将引发运行时异常:”

    • “TC 和 RC 是相同的原始类型。”

    • “TC 和 RC 是引用类型,类型 RC 可以通过递归应用这些运行时规则来转换为 TC。”

在这种情况下,TC 是int[]并且 RC 是Object并且Object 不能强制转换为int[]. 因此你得到一个例外。

为了说明为什么会发生这种情况,请考虑这个(假设的)示例:

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

假设可以“转换” 的值foo,那么mystery变量应该指向什么?这不可能是int[]因为我们没有分配一个。它不能是一个Object实例,因为这会破坏静态类型。

事实上,“强制转换”必须是非法的,否则 Java 的静态类型系统就会崩溃。


您的示例和我的示例之间的区别在于 foo 实际上不是整数数组的数组,而在我的示例中它是。为什么虚拟机看不到对象的“实际”类型?

关键是静态类型(主要)由编译器而不是 JVM 强制执行。虽然 JVM 可以(理论上)确定类型在运行时是正确的(如果不是,则抛出异常),但编译器无法做到这一点,因为它通常无法确定类型将是什么。


不幸的是,我实际上并没有自己创建数组。我从另一个函数中得到它,据我所知,它Object[]不是int[][]我无法控制的一些序列化的副作用。

不幸的是,您必须复制数组...或将其用作 anObject[]并将元素转换int[]为使用它们;例如

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

Java中没有“相信我,这确实是一个int[][]”选项。

如果这是序列化的副作用,那么序列化就被破坏了……在某种意义上。当然,标准的 Java 对象序列化协议/实现不会发生这种情况。这保留了对象的类型......除非您在readObject/writeObject方法等中明确覆盖它们。

于 2013-09-17T01:35:39.110 回答
1

由于foo是对象数组,这意味着它可以包含任何东西,而不仅仅是整数数组,所以编译器不能只因为你知道它在里面,就允许你将它转换为整数数组。

如果要使用int[][] bar引用,则必须先创建单独int [3][]的数组并将其内容复制foo到该数组。

于 2013-09-17T01:35:30.627 回答
1

您需要在访问每个对象时对其进行转换。

    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]);

try ... catch如果您认为内容可能是非整数,您还应该将其包装在 中。

编辑:简化代码。

于 2013-09-17T01:39:54.347 回答