Явное автобоксирование Java и тернарное безумие оператора

Просто потратил разочарование пару часов отладки этого кода:

    LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
    Integer boxedPci = 52;
    Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;

Вышеописанное создает исключение NullPointerException. В приведенном ниже коде нет:

    LinkedHashMap<String, Integer> rsrqs = new LinkedHashMap<String, Integer>();
    Integer boxedPci = 52;
    Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : Integer.valueOf(-1);

Единственное различие заключается в том, чтобы обернуть -1 с помощью Integer.valueOf(). Я уверен, что я буду ударить по лбу, когда кто-нибудь объяснит, почему этот код ведет себя так, как он это делает. Но может ли кто-нибудь объяснить мне, почему этот код ведет себя так:)?

- Редактировать

Во-вторых, я подозреваю, что NPE исходит из rsrqs.get(), возвращающего null, который, я думаю, java пытается распаковать в int, прежде чем снова вернуться к Integer. Integer.valueOf() заставляет Java выполнять шаг unbox-box. Мораль истории; не просто игнорируйте эти предупреждения бокса в Eclipse;)

Ответы

Ответ 1

Тернарные выражения, как и любое выражение, имеют тип, который определяется компилятором. Если две стороны тройного выражения имеют то, что выглядит как разные типы, тогда компилятор попытается найти общий базовый тип, используя наименее двусмысленные два варианта. В вашем случае -1 является наименее неоднозначным, поэтому тип тройного выражения int. К сожалению, компилятор не использует вывод типа на основе принимающей переменной.

Выражение rsrqs.get(boxedPci.toString()) затем оценивается и принудительно вводится в тип int, чтобы соответствовать тройному выражению, но поскольку оно null выбрасывает NPE.

По боксу -1 значение тройного выражения Integer, и поэтому вы небезопасны.

Ответ 2

Объяснение можно сделать из информации в спецификации языка Java: 15.25. Условный оператор?:.

Из таблицы вы получите информацию о том, что если второй операнд (rsrqs.get(boxedPci.toString())) имеет тип Integer, а третий операнд имеет тип int, результат будет иметь тип int.

Это, однако, означает, что

Integer boxedRsrq = boxedPci != null ? rsrqs.get(boxedPci.toString()) : -1;

семантически совпадает с

Integer boxedRsrq = boxedPci != null ? ((int)rsrqs.get(boxedPci.toString())) : -1;

Но это означает, что вы получаете NullPointerException, если вы получаете null с карты, что, очевидно, происходит.

Если вы передадите третий операнд в Integer, второй операнд никогда не будет передан в int, и никакой NPE не произойдет.

Ответ 3

1

- это int, а не целое число. Таким образом, Java собирается удалить ядро ​​Integer с int, что вызывает исключение NullPointerException. Когда вы автоматически удаляете нулевое целое число, это приводит к исключению NullPointerException. (ссылка здесь)

Но когда вы используете

 Integer.valueOf(-1) 

ему не нужно автоматически распаковывать его, что не приводит к исключениям.

Ответ 4

Ну, Integer.valueOf(String) возвращает Integer, а -1 - примитив int. Первый пример принудительно распаковывается, потому что один термин является примитивным. Вы также могли бы использовать

Integer boxedRsrq = boxedPci != null ? 
    rsrqs.get(boxedPci.toString()) : (Integer) -1;

в котором будет помещен символ -1.

Ответ 5

Я запускаю код и нет npe.