Java:为什么我不能将数组声明为简单对象?

12

在Java中,我可以编译代码

Object[] obj = {new Object[1], new Object[2]};

但是我无法编译

Object obj = {new Object(), new Object()};
在第一个示例中,我声明了一个由对象组成的一维数组并为其分配了一个二维数组。在第二个示例中,我声明了一个对象并为其分配了一个一维数组。
如果Java数组扩展了对象,那么为什么第二个代码片段无法编译?为什么第一个可以编译?

4个回答

13

将数组分配给对象并不是问题,但您必须像这样创建数组

Object obj = new Object[] { new Object(), new Object[2] };
否则编译器无法辨别这是一个对象数组而不是其他类型的数组。

1
我认为那并不完全正确。我认为 Object obj = {new Object(), ...} 返回一个数组,就像“多重指针数组”,而 new ... 返回一个指向 new 后面的任何东西的指针。所以 Object obj = new Object[]{...} 返回一个指向数组的指针,而 Object obj = {...} 返回一个数组。 - Danyel
2
@Danyel:在Java中,变量实际上从未包含数组,它始终包含对数组的引用(松散地说,是“指针”)(与任何其他对象一样)。 - T.J. Crowder
@Crowder:是的,从技术上讲,就是这样做的,但这并不意味着内部也是这样做的,对吧?我是说这就是编译器可能在解释时遇到问题的原因。这只是一些假设,所以除非我们得到JLS的引用,否则争论毫无意义。(尽管... JLS是否提供有关如何实现/内部操作的信息?) - Danyel
@Danyel:JLS定义了变量的持有方式。内部必须按照这种方式进行处理,否则语言的语义将会崩溃。考虑一下这个例子:Object[] a; Object[] b; a = new Object[] { new Object(), new Object() }; b = a; b[0] = null; System.out.println("a[0] is null? " + (a[0] == null));如果变量实际上包含数组,那么b = a;这一行必须是一个数组复制操作,不是吗?因此对b[0]的赋值对a[0]没有影响。但事实并非如此,因为Java定义了ab持有引用。:-) - T.J. Crowder
是的,我知道。实际上我并没有怀疑/质疑那个。对我来说,最终的问题是,Object[] o = {...} 是存储在应用程序的 Stack 还是 Heap 中。我想这就是我试图了解的地方。我知道最终 o 只会是一个引用而不是实际的数组。希望你知道我的意思。 - Danyel

9
因为Array不仅仅是Object的子类。数组还具有语言级别的语义和语法

另外,你的第二个例子引出了一个问题:你想初始化的这两个东西会存储在哪里?你刚刚声明了一个对象,但它没有命名字段,也缺乏数字索引槽,就像数组一样。

你的第一个例子编译通过,因为你声明了一个Object数组(即对象引用),并且你要给它初始化的元素都是对象引用(通过new Object[1]创建单元素数组所得到的引用)。

这可能也会有所帮助: Java实际上并没有二维数组,尽管有一些方便的语法使其看起来像是有的。它有单(一维)数组和单(一维)数组(等等)。


数组是 Object 的子类:http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Array.html - Robber
1
@Robber:我说的是“不仅仅”。 - T.J. Crowder
1
"数组不仅仅是Object的子类" => 但它确实是一个子类,你同意吗?所以你没有解释为什么这行不通,事实上它是可以行得通的(参见Robber的答案)。 "数组还具有语言级别的语义和语法" => 模糊地引用整个JLS数组章节只是恐吓证明。 "你刚刚声明了一个对象,但它没有命名字段,并且缺少数字索引插槽数组所具有的字段。" => 你没有“声明一个对象”;你声明了一个类型为Object的变量,它可以指向任何Object实例。变量从来没有字段。 - Boann
@Boann:我同意Robber似乎比我更成功地抓住了问题的相关方面,因此+1给他/她!原始(编辑前)的问题不太清楚,但我认为即使从那个时候开始,Robber也能够理解它。 - T.J. Crowder
谢谢,你写的很多内容都非常清晰明了。@Boann:如果我能想出这样一个尖锐的表述,我会喜欢回复你的。 - Student

1
问题在于当您使用初始化程序创建数组时,编译器需要通过检查元素的类型与提供的类型进行比较来确保初始化程序中的所有元素都是相同的提供类型。
也就是说,在初始化数组时始终需要提供类型信息。否则,编译器不知道如何验证数组初始化是否有效,从而产生“非法初始化程序错误”。
将数组分配给对象没有问题。例如,您可以执行以下操作:
int[] arr = {1,2};
Object obj = arr;

以下代码无法编译:

以下是代码:

Object obj = {1,2};

因为您没有明确提供编译器需要验证初始化器中的值的元素类型。这在Java中对于数组初始化是必需的。以下代码将编译:
Object[] obj = {1,2};

因为元素的类型已经提供(即Object),编译器会检查12的类型是否与Object相匹配(由于IntegerObject的子类型,因此匹配成功)。


0

无法编译

Object obj = {new Object(), new Object()};

因为 obj 不是一个数组,所以要像这样声明

Object[] obj = {new Object(), new Object()};

或者

Object obj = new Object[] { new Object(), new Object() };

在下面的对话后进行了编辑


抱歉,小偷的回答证明了你的答案是错误的。无论如何,感谢你提供答案。 - Student
1
@vamsilp:Object变量可以持有数组的引用,因为数组是从Object派生而来的。您不必声明变量Object[](尽管如果这样做可能更有用)。只是在创建和初始化数组时,编译器需要知道它是什么类型的数组,因此出现了Robber提供的语法。 - T.J. Crowder
@T.J Crowder 现在你说 Object 变量可以保存对数组的引用,我不必将变量声明为 Object[],那么为什么我不能继续声明一个 String string=new String[] {new String(),new String()} 呢?即使 String 也是一个对象,在这里 string 是一个 String 变量。为什么 Object obj = new Object[] { new Object(), new Object() }; 行得通,但同样的方法对于 String 就行不通呢? - viswanath patimalla
@vamsilp:Array不是String的子类。 - T.J. Crowder
1
@学生:我想我们两个都对,也都错了,因为我只回答了你问题中的一个选项。我会继续编辑我的答案,包括Robbers版本,以使其更清晰明了。 - viswanath patimalla
显示剩余3条评论

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