为什么返回null并分配给引用类型的三元条件表达式会导致NullPointerException?
我喜欢在java编程中使用三元条件表达式,但我遇到了一个问题:
以下代码是一个小例子,但它显示了我找到的问题。
public class Example { public Example() { Double x = 0.0; A a = new A(); x = a == null ? 0.0 : a.getY(); // Happens on this line System.out.println(x); } class A { Double y = null; private Double getY() { return y; } } public static void main(String[] args) { new Example(); } }
导致NullPointerException
什么?
这是由确定三元条件表达式类型的JLS规则产生的:
如果第二个和第三个操作数之一是原始类型T,而另一个操作数的类型是对T应用装箱转换(第5.1.7节)的结果,那么条件表达式的类型是T.
此规则意味着三元表达式的类型是double
而不是Double
。 将a.getY()
方法返回的Double
解包为double
会导致NullPointerException
,因为该方法返回null
。
a == null ? 0.0 : a.getY(); double Double -> hence the type of the ternary expression is double
这是因为0.0
是double
类型,而不是Double
。 条件运算符的后两个操作数必须是相同的类型,因此自动装箱/取消装箱进入它并且编译器将该代码转换为:
x = Double.valueOf(a == null ? 0.0 : a.getY().doubleValue()); // -^^^^^^^^^^^^^^^--------------------------^^^^^^^^^^^^^^
…因为a.getY()
返回null
而抛出,然后代码尝试在null
上调用doubleValue
。
如果我们运行javap -c Example
来反编译代码,我们可以看到那些调用(我把它们换成粗体):
公共类示例{ public Example(); 码: 0:aload_0 1:invokespecial#1 //方法java / lang / Object。“”:()V 4:dconst_0 5:invokestatic#2 //方法java / lang / Double.valueOf:(D)Ljava / lang / Double; 8:astore_1 9:new#3 // class示例$ A 12:dup 13:aload_0 14:invokespecial#4 //方法示例$ A。“”:( LExample;)V 17:astore_2 18:aload_2 19:ifnonnull 26 22:dconst_0 23:转到33 26:aload_2 27:invokestatic#5 //方法示例$ A.access $ 000 :( LExample $ A;)Ljava / lang / Double; 30:invokevirtual#6 //方法java / lang / Double.doubleValue :()D 33:invokestatic#2 //方法java / lang / Double.valueOf:(D)Ljava / lang / Double; 36:astore_1 37:getstatic#7 // Field java / lang / System.out:Ljava / io / PrintStream; 40:aload_1 41:invokevirtual#8 //方法java / io / PrintStream.println:(Ljava / lang / Object;)V 44:回归 public static void main(java.lang.String []); 码: 0:new#9 // class示例 3:重复 4:invokespecial#10 //方法“”:()V 7:流行 8:回归 }