NullPointerException,在三元表达式中具有自动装箱功能

时间:2010-07-16 14:36:01

标签: java nullpointerexception autoboxing

运行以下Java代码:

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

为什么会出现NullPointerException?

4 个答案:

答案 0 :(得分:33)

条件表达式b ? d1.doubleValue : d2的返回类型是double。条件表达式必须具有单个返回类型。遵循二进制数字促销规则,d2会自动装箱到double,这会导致NullPointerException d2 == null。{/ p>

从语言规范,§15.25部分:

  

否则,如果是第二个和第三个   操作数有类型   可转换(第5.1.8节)到数字类型,   然后有几种情况:......

     

否则,将应用二进制数字提升(第5.6.2节)   到操作数类型,以及类型   条件表达式是   推广第二和第三类型   操作数。请注意二进制数字   促销执行拆箱转换   (§5.1.8)和价值集转换   (§5.1.13)。

答案 1 :(得分:15)

因为:周围的两个表达式必须返回相同的类型。这意味着Java尝试将表达式d2转换为double。这意味着字节码在doubleValue()上调用d2 - > NPE。

答案 2 :(得分:4)

通常应该避免混合类型计算;将此与?:条件/三元混合只会使情况变得更糟。

以下是来自Java Puzzlers,引文8:Dos Equis:

的引用
  

混合型计算可能令人困惑。这比条件表达更明显。 [...]

     

确定条件表达式的结果类型的规则太长且复杂,无法完整复制,但这里有三个关键点。

     
      
  1. 如果第二个和第三个操作数具有相同的类型,那么这是条件表达式的类型。换句话说,你可以通过避开混合型计算来避免整个混乱。

  2.   
  3. 如果其中一个操作数的类型为 T ,其中 T byteshortchar ,另一个操作数是int类型的常量表达式,其值在 T 类型中具有代表性,条件表达式的类型为 T

  4.   
  5. 否则,二进制数字提升将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。

  6.   

此处应用了第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的引用,第49项:首选原始类型为盒装基元

  

总之,只要有选择,就可以优先使用原始元素。原始类型更简单,更快捷。如果你必须使用盒装基元,小心!自动装箱减少了使用盒装基元的冗长,但没有降低危险。当您的程序将两个盒装基元与==运算符进行比较时,它会进行身份比较,这几乎肯定不是您想要的。当您的程序执行涉及盒装和未装箱原语的混合类型计算时,它会进行拆箱,当您的程序取消装箱时,它可以抛出NullPointerException。最后,当您的程序框原始值时,它可能导致代价高昂且不必要的对象创建。

有些地方你别无选择,只能使用盒装基元,例如:泛型,但除此之外你应该认真考虑是否有合理使用盒装基元的决定。

相关问题

答案 3 :(得分:0)

为以下两种情况返回相同的类型,您将得到结果。

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