Почему выражение доступа к массиву нулевой ссылки на массив не генерирует исключение NullPointerException?

Рассмотрим следующий код:

int[] r = null;
r[0] = 1 % 0;

Я бы ожидал, что это бросить NullPointerException: в соответствии с JLS Sec 15.7.1:

Похоже, что левый операнд бинарного оператора полностью вычисляется перед тем, как вычисляется любая часть правого операнда.

= является бинарным оператором (показан в JLS Sec 15.2 - JLS Sec 15.26 описывает операторы присваивания), и полная оценка левого операнда приведет к NullPointerException, Тем не менее, ArithmeticException выдается, указывая, что правый операнд вычисляется до того, как левый операнд будет полностью оценен.

Зачем?

1 ответ

Спецификация простого оператора присваивания описывает это поведение:

...

Если левый операнд является выражением доступа к массиву (§15.10.3), возможно, заключенным в одну или несколько пар скобок, то:

  • Сначала оценивается подвыражение ссылки на массив выражения доступа к массиву левого операнда. Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине; подвыражение индекса (выражения доступа к массиву левого операнда) и правый операнд не оцениваются, и назначения не происходит.

Это завершается нормально.

  • В противном случае вычисляется подвыражение индекса выражения доступа к массиву левого операнда. Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине, и правый операнд не оценивается, и назначение не происходит.

Это завершается нормально.

  • В противном случае вычисляется правый операнд. Если эта оценка завершается преждевременно, то выражение присваивания завершается преждевременно по той же причине, и назначение не происходит.

Это завершается внезапно, с ArithmeticException,

  • В противном случае, если значение подвыражения ссылки на массив равно нулю, присваивание не происходит и генерируется исключение NullPointerException.

Это никогда не выполняется.

Итак, похоже, что в цитате из раздела 15.7.1 есть несоответствие - или, по крайней мере, чрезмерное упрощение.


Интересно, что такое же поведение не наблюдается для составных операторов присваивания, например

int[] arr = null;
arr[0] += 1 % 0;

действительно дает NullPointerException,

JLS Sec 15.26.2 описывает это. Это, возможно, менее удивительно, потому что:

Составное выражение присваивания формы E1 op= E2 эквивалентно E1 = (T) ((E1) op (E2)), где T это тип E1, Кроме этого E1 оценивается только один раз.

Другими словами, этот код (примерно) эквивалентен:

arr[0] = arr[0] + 1 % 0;

Итак NullPointerException происходит при оценке правого операнда простого присваивания.

Другие вопросы по тегам