最近的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