Статьи

JDK 8 против JDK 10: разница в троичной системе / распаковке

Недавний твит Николая Парлога ( @nipafx ) привлек мое внимание, потому что он ссылался на интересное обсуждение StackOverflow об изменении поведения между JDK 8 и JDK 10 и спрашивал «Почему?» Проблема, процитированная в потоке StackOverflow SerCe, в конечном итоге сводилась к тому, что реализация менялась между JDK 8 и JDK 10 для правильной реализации спецификации языка Java.

Следующий листинг кода (очень незначительно) адаптирован из исходного примера, предоставленного SerCe в потоке StackOverflow.

Адаптированный пример, который ведет себя по-разному в JDK 10 по сравнению с JDK 8

01
02
03
04
05
06
07
08
09
10
11
12
public static void demoSerCeExample()
{
   try
   {
      final Double doubleValue = false ? 1.0 : new HashMap<String, Double>().get("1");
      out.println("Double Value: " + doubleValue);
   }
   catch (Exception exception)
   {
      out.println("ERROR in 'demoSerCeExample': " + exception);
   }
}

Когда вышеприведенный код компилируется и выполняется с JDK 8, он генерирует вывод следующим образом:
Double Value: null
Когда вышеприведенный код компилируется и выполняется с JDK 10, он генерирует вывод следующим образом:
ERROR in 'demoSerCeExample': java.lang.NullPointerException
В JDK 8 троичный оператор возвратил null для назначения локальной переменной doubleValue , но в JDK 10 вместо того же троичного оператора вместо этого doubleValue NullPointerException .

Два изменения этого примера приводят к некоторым интересным наблюдениям. Во-первых, если литеральная константа 1.0 выраженная в троичном операторе, указана вместо этого как Double.valueOf(1.0) , то и JDK 8, и JDK 10 устанавливают для локальной переменной значение null а не выбрасывают исключение NullPointerException . Во-вторых, если локальная переменная объявлена ​​с типом примитива double вместо ссылочного типа Double , NullPointerException всегда Double.valueOf(double) независимо от версии Java и независимо от того, Double.valueOf(double) ли Double.valueOf(double) . Это второе наблюдение имеет смысл, конечно, потому что независимо от того, как объект или ссылка обрабатывается троичным оператором, в некоторой точке его необходимо разыменовать, чтобы присвоить примитиву типа double и это всегда приведет к NullPointerException в примере. ,

Следующая таблица суммирует эти наблюдения:

Полное троичное заявление Установка локальной переменной doubleValue
JDK 8 JDK 10
1
2
3
4
Double doubleValue
= false
? 1.0
: new HashMap<String, Double>().get("1");
null NullPointerException

1
2
3
4
double doubleValue
   false
    ? 1.0
    : new HashMap<String, Double>().get("1");
NullPointerException NullPointerException
1
2
3
4
Double doubleValue
   false
    ? Double.valueOf(1.0)
    : new HashMap<String, Double>().get("1");
null null
1
2
3
4
double doubleValue
   false
    ? Double.valueOf(1.0)
    : new HashMap<String, Double>().get("1");
NullPointerException NullPointerException

Единственный подход, который избегает NullPointerException в обеих версиях Java для этого общего троичного примера, — это версия, которая объявляет локальную переменную как ссылочный тип Double (принудительная распаковка не выполняется) и использует Double.valueOf(double) так что ссылка Double используется повсеместно троичный, а не примитивный double . Если примитивный double подразумевается при указании только 1.0 , то Double возвращаемый Java Map , неявно распаковывается (разыменовывается) в JDK 10, что приводит к исключению. По словам Брайана Гетца , JDK 10 возвращает реализацию в соответствие со спецификацией.

Опубликовано на Java Code Geeks с разрешения Дастина Маркса, партнера нашей программы JCG . См. Оригинальную статью здесь: JDK 8 против JDK 10: разница в троичной системе / распаковке

Мнения, высказанные участниками Java Code Geeks, являются их собственными.