Недавний твит Николая Парлога ( @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 | |||
|
null |
NullPointerException |
||
|
NullPointerException |
NullPointerException |
||
|
null |
null |
||
|
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, являются их собственными. |