NullPointerException с автобоксом в троичном выражении - PullRequest
21 голосов
/ 16 июля 2010

Запустите следующий код Java:

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;

Почему существует исключение NullPointerException?

Ответы [ 4 ]

33 голосов
/ 16 июля 2010

Тип возврата условного выражения b ? d1.doubleValue : d2: double.Условное выражение должно иметь один тип возвращаемого значения.Следуя правилам двоичного числового продвижения, d2 автоматически добавляется в double, что вызывает NullPointerException при d2 == null.

Из спецификации языка, раздел §15.25:

В противном случае, если второй и третий операнды имеют типы, которые могут быть преобразованы (§5.1.8) в числовые типы, то есть несколько случаев: ...

В противном случае, двоичное числовое продвижение (§5.6.2) применяется к типам операндов, а тип условного выражения является продвинутым типом второго и третьего операндов.Обратите внимание, что двоичное числовое продвижение выполняет преобразование без коробки (§5.1.8) и преобразование набора значений (§5.1.13).

15 голосов
/ 16 июля 2010

Поскольку два выражения вокруг : должны возвращать один и тот же тип. Это означает, что Java пытается преобразовать выражение d2 в double. Это означает, что байт-код вызывает doubleValue() на d2 -> NPE.

4 голосов
/ 16 июля 2010

Вы должны вообще избегать вычисления смешанного типа;соединение с ?: условно / троично только усугубляет ситуацию.

Вот цитата из Java Puzzlers , Puzzle 8: Dos Equis:

Вычисления смешанного типа могут сбивать с толку.Нигде это не так очевидно, как условное выражение.[...]

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

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

  2. Если один из операндов имеет тип T , где T равен byte, short или char, а другой операнд является константным выражением типа int, значение которого представимо в типе T , типе условного выраженияВыражение: T .

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

Точка 3 применяется здесь, и это привело к распаковке.Когда вы распаковываете null, естественно, выдается NullPointerException.

Вот еще один пример вычислений смешанного типа и ?:, что может удивить:

    Number n = true ? Integer.valueOf(1) : Double.valueOf(2);

    System.out.println(n); // "1.0"
    System.out.println(n instanceof Integer); // "false"
    System.out.println(n instanceof Double);  // "true"

Смешанный типвычисление является предметом как минимум 3 Java Puzzlers .

В заключение вот что Java Puzzlers предписывает:

4.1.Расчеты смешанного типа сбивают с толку

Предписание : Избегайте вычислений смешанного типа.

При использовании оператора ?: с числовыми операндами используйте один и тот же числовой тип для обоихвторой и третий операнды.


О предпочтении примитивных типов примитивам в штучной упаковке

Вот цитата из Effective Java 2nd Edition, Item 49: Предпочитают примитивные типыв штучной упаковке примитивы :

В итоге, используйте примитивы вместо коробочных примитивов, когда у вас есть выбор.Примитивные типы проще и быстрее.Если вы должны использовать коробочные примитивы, будьте осторожны!Автобокс уменьшает многословность, но не опасность использования коробочных примитивов.Когда ваша программа сравнивает два коробочных примитива с оператором ==, она выполняет сравнение идентификаторов, что почти наверняка не то, что вам нужно.Когда ваша программа выполняет вычисления смешанного типа с использованием коробочных и распакованных примитивов, она выполняет распаковку, а когда ваша программа выполняет распаковку, она может выдать NullPointerException.Наконец, когда ваша программа упаковывает примитивные значения, это может привести к дорогостоящим и ненужным созданиям объектов.

Есть места, где у вас нет выбора, кроме как использовать примитивы в штучной упаковке, например, дженерики, но в противном случае вам следует серьезноподумайте, оправдано ли решение использовать штучные примитивы.

Смежные вопросы

0 голосов
/ 08 июля 2019

Верните одинаковый тип для обоих условий, как показано ниже, и вы получите результат.

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1 : (Double)d2;
System.out.println(d);
...