如何将一个对象数组转换为一个整数二维数组

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在生产中可能非常大,因此我可能不想这样做)?如果不能,为什么不能?

请尝试访问https://dev59.com/JnNA5IYBdhLWcg3wC5NR#8220245。 - ironwood
谢谢,但原本的目标是不复制完成。 - Catherine
3个回答

11
您不能通过将其强制转换来更改Java对象的固有类型,包括数组。对象的类型在分配对象时固定,不能更改。
因此,将Object[]强制转换为int[][]永远不起作用。
在这种情况下,尽管int[][]是Object[]的子类型,但这不足以允许类型转换在运行时被允许。 JLS中的实际规则(5.5.3. Checked Casts at Run Time)是这样的:
以下是该算法的翻译,用于检查对象的运行时类型R是否与强制转换操作符中命名类型的消除(§4.6)类型T兼容。如果抛出运行时异常,则为ClassCastException。 如果R是表示数组类型RC [](即类型RC的组件的数组)的类:如果T是数组类型TC [](即类型TC的组件的数组),则除非满足以下条件之一,否则会抛出运行时异常:1.TC和RC是相同的基本类型; 2.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[][]是我无法控制的某种序列化的副作用,据我所知。

不幸的是,您必须将数组复制一份... 或者将其作为Object[]使用,并将元素强制转换为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方法中明确覆盖它们等。

只是试图获得对此的坚实理解,显然您关于其功能方式是正确的-您的示例与我的区别在于foo实际上不是整数数组的数组,而在我的情况下它是。为什么虚拟机无法看到对象的“实际”类型? - Catherine
虽然正确,但这并没有解决 OP 的问题。解决方案是访问 int[],正如我谦逊地提交的答案所示 :-) - andy256
@andy256 强制类型转换肯定可以解决问题,但我也非常好奇为什么这样做不行,所以这个信息非常有用。 - Catherine
2
@andy256 - 不,正确的答案是这样做:Object[] foo = new int[3][]; ...并且去掉类型转换。 - Stephen C
1
@arshajii - 你对子类型的理解是正确的,但事实证明,根据JLS,导致失败的真正原因根本不涉及子类型。 (事实上,子类型解释了为什么这不是编译时错误!)我已经相应地更正了我的答案。 - Stephen C
显示剩余5条评论

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中。
编辑:简化代码。

1

由于foo是对象数组,这意味着它可以包含任何内容,而不仅仅是整数数组,因此编译器不能允许您将其强制转换为整数数组,只因为您知道它在内部。

如果您想使用int[][] bar引用,则需要先创建一个单独的int [3][]数组,并将foo的内容复制到该数组中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接