NullPointerException через авто-бокс-поведение троянного оператора Java
На днях я столкнулся с действительно странным NullPointerException
, вызванным неожиданным типом в тернарном операторе. Учитывая эту (бесполезную примерную) функцию:
Integer getNumber() {
return null;
}
Я ожидал, что следующие два сегмента кода будут точно идентичными после компиляции:
Integer number;
if (condition) {
number = getNumber();
} else {
number = 0;
}
против.
Integer number = (condition) ? getNumber() : 0;
.
Выключается, если condition
- true
, условие if
работает отлично, а троичный отступ во втором сегменте кода вызывает NullPointerException
. Кажется, что тройная операция решила набрать оба варианта до int
, прежде чем автоматически поместить результат в Integer
!?! Фактически, если я явно применяю 0
к Integer
, исключение исчезает. Другими словами:
Integer number = (condition) ? getNumber() : 0;
- это не то же самое, что:
Integer number = (condition) ? getNumber() : (Integer) 0;
.
Итак, кажется, что существует байт-кодовое различие между тернарным оператором и эквивалентным if-else
-statement (чего я не ожидал). Что вызывает три вопроса: почему существует разница? Является ли это ошибкой в тройной реализации или есть причина для типа cast? Учитывая, что существует разница, тройная операция более или менее эффективна, чем эквивалентное if
-стояние (я знаю, разница не может быть огромной, но все же)?
Ответы
Ответ 1
Согласно JLS: -
Тип условного выражения определяется следующим образом:
- Если второй и третий операнды имеют один и тот же тип (который может быть нулевым типом), то это тип условного выражение.
- Если один из второго и третьего операндов имеет примитивный тип T, а тип другого - результат применения преобразования бокса (П. 5.1.7) в T, то тип условного выражения равен T.
Ответ 2
Проблема в том, что:
Integer number = (condition) ? getNumber() : 0;
Принудительно распаковывает и переустанавливает результат getNumber(). Это связано с тем, что ложная часть тройки (0) является целым числом, поэтому она пытается преобразовать результат getNumber() в int. Принимая во внимание следующее:
Integer number = (condition) ? getNumber() : (Integer) 0;
Это не ошибка, а именно то, как Java решила что-то сделать.
Ответ 3
Вот как он должен работать. Тернарный оператор не должен быть эквивалентен регулярному выражению if
. Телами if
и else
являются операторы, а части, следующие за ?
и :
, являются выражениями, которые должны оцениваться одним и тем же типом.
Другими словами: a = b ? c : d
не должен быть эквивалентен if (b) a = c; else a = d;
. Вместо этого b ? c : d
является выражением само по себе, а присвоение его результата a
не повлияет на результат.