什么是允许“new int[] {0}[0] = 1;”编译的Java语法?

15
我正在使用Java®语言规范 Java SE 8版作为参考。
示例类:
class MyClass {

  void method() {
    new int[] {0}[0] = 1;
  }

}

代码 new int[] {0}[0] = 1 应该是一个赋值语句,因为创建的数组的索引0被赋值为1
一个赋值语句由左操作数、赋值运算符和表达式组成。在这个例子中,左操作数应该是new int[] {0}[0]
左操作数要么是表达式名,要么是字段访问,要么是数组访问。在这个例子中,左操作数应该是一个数组访问。
问题出现在数组访问上。数组访问被定义为是一个表达式名(不适用于这个例子)或者一个PrimaryNoNewArray,然后是括号中的一个表达式。
代码`new int[] {0}`是一个ArrayCreationExpressionPrimary表达式可以是ArrayCreationExpression或PrimaryNoNewArray。因此,对我来说,ArrayAccess的第二种情况应该是Primary而不是PrimaryNoNewArray。
我知道JLS并没有为所有内容都有明确的语法,例如带括号的Expressions或带括号的LeftHandSides,但这似乎是一个错误。我检查了最新的规范(Java SE 17),ArrayAccess的语法没有改变。

有点过于追求细节,但仍是一个有趣的观察。就我个人而言,我认为这应该是一个编译错误,因为据我所见,代码是无用的,因为创建的数组是无法访问的。它唯一的潜在影响只是对内存的副作用,如果编译器没有将其删除的话。 - xtratic
@xtratic 你也可以在while循环条件中使用它,但那同样是无用的。while ((new int[] {0}[0] = 1) > 0) { /* 做一些事情 */} - Geoff
@xtratic 严格来说,它并不是“无用”的,因为它允许表达式按照语句的顺序进行排序,以便在需要单个表达式的上下文中使用其副作用,例如 new int[] {x += 5, y -= 5, y /= x, x *= y}[3]。显然这是一个糟糕的用法,但从技术上讲,它是一种用法。 - kaya3
@Geoff 是的,你可以在任何可以使用表达式结果(在这种情况下为1)的地方使用它(new int[] {0}[0] = 1),但这肯定仍然是无用的,因为你仍然可以只使用赋值操作符右侧的表达式。你的例子基本上与while (1 > 0) { /* Do stuff */}相同,除了可能(不可达)的内存副作用。 - xtratic
2
你写道:“我知道JLS并没有为所有内容都提供明确的语法,例如带括号的表达式或带括号的左侧”,但实际上,带括号的表达式在JLS&15.8中被列为PrimaryNoNewArray,这导致结论是javac允许,例如(x) = 4;是另一个明显违反规范的行为。 - Holger
显示剩余2条评论
1个回答

9
严格来说,这似乎不符合指定的语法规范。但至少,编译器允许这样做是有道理的:在此处使用`PrimaryNoNewArray`而不是`Primary`是为了避免像`new int[1][0]`这样的表达式被解析为二维数组或者像`(new int[1])[0]`这样的数组访问。如果数组带有类似于`new int[]{0}[0]`这样的初始化,则语法不会存在歧义,因为它不能被解析为创建一个二维数组。
也就是说,尽管Java语言规范没有明确允许这种语法,但可以将其视为实现细节或者编译器中的缺陷。

如果将ArrayCreationExpression分成两个不同的产生式(一个包含包含ArrayInitializer的两种情况,另一个包含不包含ArrayInitializer的两种情况),那么是否可以更新ArrayAccess以包括ExpressionName、PrimaryNoNewArray和ArrayCreationExpressionWithInitializer,而不会引入任何歧义? - Geoff
1
@Geoff 是的,就我所知,那不会造成任何歧义。 - kaya3

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