最近的Nicolai Parlog ( @nipafx ) 鸣叫引起了我的注意,因为它引用了关于JDK 8和JDK 10之间行为更改的有趣StackOverflow讨论 ,并询问“为什么?” SerCe 在StackOverflow线程上引用的问题最终归结为在JDK 8和JDK 10之间更改了实现,以正确实现 Java语言规范。
下面的代码清单(略有改编)改编自SerCe在StackOverflow线程上提供的原始示例。
在JDK 10和JDK 8中表现不同的改编示例
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中,为同一三元语句抛出NullPointerException
。
此示例的两个调整导致一些有趣的观察。 首先,如果将三元运算符中表示的文字常量1.0
指定为Double.valueOf(1.0)
,则JDK 8和JDK 10都将局部变量设置为null
而不是抛出NullPointerException
。 其次,如果使用原始类型double
而不是引用类型Double
声明了局部变量,则无论Java版本和是否使用Double.valueOf(double)
都始终抛出NullPointerException
。 当然,第二个观察是有道理的,因为无论三元运算符如何处理对象或引用,都必须在某个点取消引用以将其分配给原始double
类型,并且在示例中始终会导致NullPointerException
。
下表总结了这些观察结果:
完整的三元声明 | 设置局部变量doubleValue | |
JDK 8 | JDK 10 | |
| null | NullPointerException |
| NullPointerException | NullPointerException |
| null | null |
| NullPointerException | NullPointerException |
对于这个一般的三进制示例,在两个Java版本中都避免NullPointerException
的唯一方法是将局部变量声明为引用类型Double
(无需取消装箱)并使用Double.valueOf(double)
以便在整个过程中都使用引用Double
三元而不是原始的double
。 如果仅通过指定1.0
隐含原始double
,则Java Map
返回的Double
在JDK 10中将被隐式取消装箱(取消引用),并导致异常。 根据Brian Goetz的说法 ,JDK 10使实现返回到符合规范的状态。
翻译自: https://www.javacodegeeks.com/2018/06/jdk-ternary-difference.html