Подтвердить что ты не робот

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)
$
4b9b3361

Ответ 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.