为什么没有对空数组引用的数组访问表达式抛出NullPointerException?

And*_*ner 5 java language-specifications

考虑以下代码:

int[] r = null;
r[0] = 1 % 0;
Run Code Online (Sandbox Code Playgroud)

我本来希望NullPointerException根据JLS Sec 15.7.1抛出

在评估右侧操作数的任何部分之前,似乎已对二进制运算符的左侧操作数进行了完全评估。

=是一个二进制运算符(如JLS Sec 15.2 -JLS Sec 15.26中所示,它描述了赋值运算符),并且对左操作数进行完全求值将得出NullPointerException。但是,将ArithmeticException引发一个,指示在对左侧操作数进行完全求值之前先评估右侧操作数。

为什么?

And*_*ner 5

简单赋值运算符的规范描述了此行为:

...

如果左侧操作数是数组访问表达式(第15.10.3节),可能包含在一对或多对括号中,则:

  • 首先,评估左侧操作数数组访问表达式的数组引用子表达式。如果该评估突然完成,则赋值表达式由于相同的原因而突然完成;(左操作数数组访问表达式的)索引子表达式和右操作数不求值,并且不发生赋值。

正常完成。

  • 否则,将评估左侧操作数数组访问表达式的index子表达式。如果该评估突然完成,则由于相同的原因,赋值表达式会突然完成,并且不会评估右侧操作数并且不会发生赋值。

正常完成。

  • 否则,将评估右侧操作数。如果该评估突然完成,则赋值表达式由于相同的原因而突然完成,并且不会发生赋值。

使用会突然完成ArithmeticException

  • 否则,如果数组引用子表达式的值为null,则不会发生分配,并且会引发NullPointerException。

永远不会执行。

因此,似乎在第15.7.1节中的引用中存在不一致(或至少过于简化)。


有趣的是,对于复合赋值运算符,未观察到相同的行为,例如

int[] arr = null;
arr[0] += 1 % 0;
Run Code Online (Sandbox Code Playgroud)

确实产生一个NullPointerException

JLS第15.26.2节对此进行了描述。不过,这也许并不奇怪,因为:

形式的复合赋值表达式E1 op= E2等效于E1 = (T) ((E1) op (E2)),其中T是的类型E1,不同之处在于该表达式E1仅被评估一次。

换句话说,此代码(大致)等效于:

arr[0] = arr[0] + 1 % 0;
Run Code Online (Sandbox Code Playgroud)

因此NullPointerException发生在评估简单赋值的右手操作数时。