Почему выражение доступа к массиву нулевой ссылки на массив не генерирует исключение 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
происходит при оценке правого операнда простого присваивания.