Почему тройная операция дает nullpointer, в то время как ее ifelse-аналог не делает?
Я получаю NullPointerException в одном экземпляре ниже, в то время как его копия работает плавно.
public static void main(String[] args){
System.out.println(withTernary(null, null)); //Null Pointer
System.out.println(withIfElse(null, null)); //No Exception
}
private static Boolean withTernary(String val, Boolean defVal){
return val == null ? defVal : "true".equalsIgnoreCase(val);
}
private static Boolean withIfElse(String val, Boolean defVal){
if (val == null) return defVal;
else return "true".equalsIgnoreCase(val);
}
Онлайн-версия
Онлайн-версия с линиями в main
в обратном порядке, которая выводит null
из withIfElse
, а затем выходит из строя withTernary
.
Я использую следующую версию java
java version "1.6.0_65"
Java(TM) SE Runtime Environment (build 1.6.0_65-b14-462-11M4609)
Java HotSpot(TM) 64-Bit Server VM (build 20.65-b04-462, mixed mode)
Ответы
Ответ 1
Здесь соответствующая цитата из спецификация (§15.25.2):
Булевы условные выражения являются автономными выражениями (§15.2).
Тип булевого условного выражения определяется следующим образом:
-
Если второй и третий операнды имеют тип Boolean
, условное выражение имеет тип Boolean
.
-
В противном случае условное выражение имеет тип Boolean
.
Следовательно, общий тип выражения считается Boolean
, а значение Boolean
является autounboxed, вызывая NullPointerException
.
Как упоминалось в комментариях, почему следующие исключения не вызывают исключения?
return val == null ? null : "true".equalsIgnoreCase(val);
Ну, вышеприведенная выдержка из спецификации специфически применима только к булевым условным выражениям, которые указаны здесь (§15.25):
Если оба выражения второго и третьего операндов являются булевыми выражениями, условное выражение является булевым условным выражением.
В целях классификации условного выражения следующие выражения являются булевыми выражениями:
-
Выражение отдельной формы (§15.2), имеющее тип Boolean
или Boolean
.
-
В скобках выражение Boolean
(§15.8.5).
-
Выражение экземпляра экземпляра класса (§15.9) для класса Boolean
.
-
Вызов вызова метода (§15.12), для которого выбранный наиболее специфический метод (§15.12.2.5) имеет тип возврата Boolean
или Boolean
.
(Обратите внимание, что для универсального метода это тип перед созданием аргументов типа метода.)
-
A Boolean
условное выражение.
Так как null
не является булевым выражением, общее условное выражение не является булевым условным выражением. Ссылаясь на таблицу 15.2 (позже в том же разделе), мы можем видеть, что такое выражение считается типом Boolean
, поэтому не происходит распаковки, и исключение не возникает.
Ответ 2
val == null ? defVal : "true".equalsIgnoreCase(val)
- в этом выражении третий аргумент boolean
, и поскольку тернарный оператор должен иметь один статический тип, он попытается распаковать null
, следовательно, NPE. Только назначение boolean
приведет к боксу снова.
Проверьте этот.
Ответ 3
Потому что autounboxing. Java выводит общий тип для defVal
и "true".equalsIgnoreCase(val)
. Тип первого - логический, но второй логический. По неизвестной причине он считает, что общий тип будет логическим (вы можете найти правило в спецификации).
Ответ 4
Это можно объяснить, если defVal
null
.
С тройными выражениями оба параметра должны быть одного типа, и если не применяется какое-либо принуждение:
JLS 15.25 "Условный оператор?:" говорит:
Если один из второго и третьего операндов имеет примитивный тип T, а тип другого - результат применения преобразования бокса (п. 5.1.7) в T, то тип условного выражения T.
В случае этого выражения:
val == null ? defVal : "true".equalsIgnoreCase(val)
Значение Boolean
для defVal
автоматически распаковывается в соответствии с результатом Boolean
сравнения строк. Это несмотря на то, что результат тройного затем автоматически помещается в квадрат обратно к Boolean
- при принятии решения о том, как бросать, тройной не считает ничего вне себя.
Ответ 5
Ok. Попробуйте таким образом
public static void main(String[] args){
System.out.println(withTernary(null, null));
}
private static Boolean withTernary(String val, Boolean defVal){
return (val == null ? defVal : Boolean.valueOf("true".equalsIgnoreCase(val)));
}
Теперь все будет хорошо. Теперь здесь нет unboxing
и вы не получите исключения.
Но в противном случае из-за null
unbox
вы получите NPE
public static void main(String[] args){
System.out.println(withTernary(null, null)); //Null Pointer
}
private static Boolean withTernary(String val, Boolean defVal){
return (val == null ? defVal : "true".equalsIgnoreCase(val));
}