Java NPE в тройном операторе с автобоксированием?
Сегодня утром я столкнулся с очень странным NPE и привел его к простому примеру. Является ли это ошибкой JVM или правильным поведением?
public class Test1 {
class Item {
Integer id = null;
public Integer getId() {return id;}
}
public Integer f() {
Item item = new Item();
// this works:
//return item == null ? new Integer(1) : item.getId();
// NPE??
return item == null ? 1 : item.getId();
}
public static void main(String[] args) {
Test1 t = new Test1();
System.out.println("id is: " + String.valueOf(t.f()));
}
}
Выход из компиляции и запуска:
$ javac Test1.java
$ java Test1
Exception in thread "main" java.lang.NullPointerException
at Test1.f(Test1.java:12)
at Test1.main(Test1.java:16)
$
Ответы
Ответ 1
Тип выражения item == null ? 1 : item.getId()
равен int
not Integer
. Поэтому Java необходимо автоматически распаковать ваш Integer
в int
(вызывая NullPointerException
). Затем он автоматически возвращает результат обратно в Integer
(ну, если не для NullPointerException
), чтобы вернуться из метода.
С другой стороны, выражение item == null ? new Integer(1) : item.getId()
имеет тип Integer
, и автоматическое удаление нежелательных сообщений не требуется.
Когда вы автоматически распаковываете a null
Integer
, вы получаете NullPointerException
(см. Autoboxing), и это то, что вы испытываете.
Чтобы ответить на ваш вопрос, это правильное поведение.
Ответ 2
item
может быть не null
, но когда вы вызываете getId()
, это возвращает null
. Когда вы пытаетесь автосообщение null
, вы получаете NPE.
Ответ 3
Если вы декомпилируете файл класса, вы увидите, что ваш NPE...
return Integer.valueOf(item != null ? item.getId().intValue() : 1);
Ответ 4
Ниже приведен тип возврата Integer
-
public Integer f() {
Item item = new Item();
// this works:
//return item == null ? new Integer(1) : item.getId();
// NPE??
return item == null ? 1 : item.getId();
}
И результат следующего -
item == null ? 1 : item.getId()
есть null
в вашем случае.
Итак, JVM бросает NPE, потому что пытается autobox null
.
Попробуйте -
new Integer(null); // and
Integer.valueOf(null);
оба бросят NPE.
Ответ 5
Это происходит потому, что вы используете условный оператор ?
. Линия
return item == null ? 1 : item.getId();
эквивалентно
int result = item == null ? 1 : item.getId();
return result;
Результат - int из-за первого операнда в вашем выражении. Именно по этой причине ваш код работает, когда вы явно переносите 1 с помощью Integer. В этом случае компилятор создает нечто вроде
Integer result = item == null ? new Integer(1) : item.getId();
return result;
Таким образом, NPE возникает при попытке "отличить" item.getId() (что равно null) от int.