В Java-тернарном операторе можно определить первый аргумент, даже если выражение привело к ложному значению?
Недавно я обнаружил необычную ошибку в моем коде случайным ad-hoc-тестированием. Итак, я сделал для этого тестовый пример.
Вот мой тестовый пример:
SampleRequest request = new SampleRequest();
request.setA(null);
request.setB(null);
assertEquals(null, request.getAOrB());
A и B определяются как типы java.lang.Integer и имеют прямые методы настройки, чтобы установить их значения в запрос.
Существует также перечисление. Он имеет примитивное целочисленное значение и метод, используемый в этом коде. Я разместил здесь следующие разделы:
enum Swapper {
public int c;
Swapper findSwapperToUse(final int a) {
for(Swapper swapper : values()) {
if(swapper.c == a) {
return swapper;
}
}
return null;
}
}
Теперь, здесь метод, который запутывает. Вызов метода теста для этого метода приводит к NPE, но в последней строке метода.
public class SampleRequest {
private Integer A;
private Integer B;
public void setA(final Integer A) {
this.A = A;
}
public void setB(final Integer B) {
this.B = B;
}
public Integer getAOrB() {
return A != null ? Swapper.findSwapperToUse(A).c
: B;
}
}
В тесте оба A и B установлены в нуль. Следовательно, A!= Null возвращает false. Тем не менее, я получаю исключение NullPointerException в номере строки для строки: B.
Я предполагаю, что по какой-то причине оценивается первое выражение Swapper.findSwapperToUse(A).c, и поэтому A.intValue() вызывается с помощью autoboxing, в результате чего NullPointerException имеет значение null. Благодаря отладке известно, что findSwapperToUse() не вызывается.
Однако, согласно этому вопросу, этого не должно произойти:
Явная трехмерная (немедленная) оценка
Выраженное выражение операнда не оценивается для этой конкретной оценки условного выражения.
Возврат null (B) не приведет к исключению NullPointerException - он отлично подходит для возврата здесь нулевого результата.
Что происходит?
EDIT: Я забыл добавить, что я изменил код, чтобы избежать этого, используя оператор "прямо вверх" if - следующий код работает как ожидалось:
public Integer getAOrB() {
if(A != null) {
return Swapper.findSwapperToUse(A).c;
}
return B;
}
Ответы
Ответ 1
Я думаю, проблема связана с тем, что компилятор выводит тип всего выражения
A != null ? Swapper.findSwapperToUse(A).c : B
как int
из типа Swapper.c
, и поэтому пытается применить преобразование unboxing в B
.
Вот связанная выдержка из JLS, §15.25:
- В противном случае, если второй и третий операнды имеют типы, которые могут быть конвертируемыми (§5.1.8) к числовым типам, то есть несколько случаев:
- ...
- В противном случае к операнду применяется двоичное числовое продвижение (§5.6.2) типы, а тип условного выражения - это продвинутый тип второго и третьего операндов. Обратите внимание, что выполняется двоичное числовое продвижение unboxing conversion (§5.1.8) и преобразование значений (§5.1.13).
Вы можете предотвратить это, добавив следующий состав:
A != null ? (Integer) Swapper.findSwapperToUse(A).c : B
Ответ 2
Ваш метод findSwapperToUse
возвращает значение null, и вы не можете сделать null.c
.
Чтобы убедиться в этом, я бы изменил ваш код:
public Integer getAOrB() {
if(A != null) {
Swapper foundSwapper = Swapper.findSwapperToUse(A);
return foundSwapper.c;
}
return B;
}