Java NPE в тройном операторе с автобоксированием?

18

Сегодня утром я столкнулся с очень странным 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)
$
    
задан Kevin 18.10.2011 в 19:57
источник
  • pst использовать Integer.valueOf (1) вместо нового Integer (1) –  ratchet freak 18.10.2011 в 20:04
  • , что является хорошим уловом с автобоксированием. Вот почему популярные рекомендации рекомендуют использовать примитивный тип поверх оболочки. –  Cygnusx1 18.10.2011 в 20:15
  • Очень похоже на странное Java NullPointerException с autoboxing –  Pops 30.04.2012 в 19:14

5 ответов

30

Тип выражения item == null ? 1 : item.getId() равен int not Integer . Поэтому Java необходимо автоматически удалить ваш Integer до int (вызывая NullPointerException ). Затем он автоматически возвращает результат обратно в Integer (ну, он будет , если не для NullPointerException ), чтобы вернуться из метода.

С другой стороны, выражение item == null ? new Integer(1) : item.getId() имеет тип Integer , и автоматическое разблокирование не требуется.

Когда вы автоматически удаляете null Integer , вы получаете NullPointerException (см. Autoboxing ), и это то, что вы испытываете.

Чтобы ответить на ваш вопрос, это правильное поведение.

    
ответ дан Jack Edmonds 18.10.2011 в 20:09
источник
  • +1 это лучшее объяснение –  Jim Garrison 18.10.2011 в 20:12
3

Если вы декомпилируете файл класса, вы увидите, что ваш NPE ...

return Integer.valueOf(item != null ? item.getId().intValue() : 1);
    
ответ дан smp7d 18.10.2011 в 20:08
источник
3

item может не быть null , но когда вы вызываете getId() , это возвращает null . Когда вы пытаетесь автоматически отключить null , вы получаете NPE.

    
ответ дан nicholas.hauschild 18.10.2011 в 20:02
источник
  • Вы уверены? getId возвращает целое число, которое имеет значение null, поэтому не должно быть автобоксинга. И если вы говорите правду, то почему возвращение нового Integer (1) заставляет NPE уйти? –  Kevin 18.10.2011 в 20:05
  • @Kevin С новым Integer (1) оба и второго операндов имеют тип Integer, поэтому это тип условного выражения. С 1 тип условного выражения - int, поэтому возвращаемое значение getId () распаковывается в int. (Проверьте правильность правил 15.25 JLS). –  dlev 18.10.2011 в 20:07
2

Ниже приведен тип возврата 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.

    
ответ дан Bhesh Gurung 18.10.2011 в 20:08
источник
2

Это происходит потому, что вы используете условный оператор ? . Линия

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.

    
ответ дан AlexR 18.10.2011 в 20:11
источник