Java条件运算符?:结果types

我对有条件的操作符有点困惑。 考虑以下两行:

Float f1 = false? 1.0f: null; Float f2 = false? 1.0f: false? 1.0f: null; 

为什么f1变成空,第二个语句抛出一个NullPointerException?

Langspec-3.0 para 15.25 sais:

否则,第二个和第三个操作数分别是S1和S2的types。 假设T1是将装箱转换为S1所得到的types,并设T2是将装箱转换为S2所得到的types。 条件expression式的types是将采集转换(§5.1.10)应用于lub(T1,T2)(§15.12.2.7)的结果。

那么对于false?1.0f:null T1是Float,T2是nulltypes。 但是lub(T1,T2)的结果是什么? 这段15.12.2.7只是有点太…

顺便说一句,我在Windows上使用1.6.0_18。

PS:我知道Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; Float f2 = false? (Float) 1.0f: false? (Float) 1.0f: null; 不扔NPE。

区别在于编译时expression式的静态types:

概要

 E1: `(false ? 1.0f : null)` - arg 2 '1.0f' : type float, - arg 3 'null' : type null - therefore operator ?: : type Float (see explanation below) - therefore autobox arg2 - therefore autobox arg3 E2: `(false ? 1.0f : (false ? 1.0f : null))` - arg 2 '1.0f' : type float - arg 3 '(false ? 1.0f : null)' : type Float (this expr is same as E1) - therefore, outer operator ?: : type float (see explanation below) - therefore un-autobox arg3 

详细说明:

这是我通过阅读规范和从你得到的结果倒退的理解。 它归结为f2 内部条件为空types的第三个操作数的types,而f2 外部条件的第三个操作数的types被认为是Float。

注意:重要的是要记住,确定的types和插入的装箱/拆箱代码是在编译时完成的。 装箱/拆箱代码的实际执行在运行时完成。

 Float f1 = (false ? 1.0f : null); Float f2 = (false ? 1.0f : (false ? 1.0f : null)); 

f1条件和f2内部条件: (false?1.0f:null)

f1条件和f2内部条件是相同的: (false?1.0f:null) 。 f1条件和f2内部条件的操作数types为:

 type of second operand = float type of third operand = null type (§4.1) 

第15.25节中的大部分规则都已经过了,这个最终的评估确实适用:

否则,第二个和第三个操作数分别是S1和S2的types。 假设T1是将装箱转换为S1所得到的types,并设T2是将装箱转换为S2所得到的types。 条件expression式的types是将采集转换( §5.1.10 )应用于lub(T1,T2)( §15.12.2.7 )的结果。

 S1 = float S2 = null type T1 = Float T2 = null type type of the f1 and f2 inner conditional expressions = Float 

因为对于f1,赋值给一个Float引用variables,expression式(null)的结果被成功赋值。

对于f2外部条件: (false?1.0f:[f2内部条件])

对于f2外部条件,这些types是:

 type of second operand = float type of third operand = Float 

注意操作数types与直接引用字符的f1 / f2内部条件( §4.1 )相比的区别。 由于具有2个数字可转换types的差异,因此来自§15.12.2.7的这个规则适用:

  • 否则,如果第二个和第三个操作数的types是可转换的( §5.1.8 )到数字types,那么有几种情况:…

    • 否则,对操作数types应用二进制数字提升(第5.6.2节 ),并且条件expression式的types是第二个和第三个操作数的提升types。 请注意,二进制数字提升会执行拆箱转换 ( §5.1.8 )和值集转换( §5.1.13 )。

由于对f2内部条件(空)的结果执行了拆箱转换,因此引发了NullPointerException。

当你试图给一个原语分配一个null时,下面将会抛出一个NPE

  float f1 = false ? 1.0f: null; 

我相信在第二个陈述中是什么造成了NPE。 因为第一个三元组返回一个真正的浮点数,所以它也试图将false转换为浮点数。

第一个语句将不会转换为null,因为所需的结果是一个Float

例如,这不会抛出一个NPE,因为它不再需要转换为原始的

  Float f = false? new Float(1.0f): true ? null : 1.0f; 

我认为重写代码使得解释更加清晰:

  float f = 1.0f; Float null_Float = false? f : null; // float + null -> OK Float null_Float2 = false? (Float)f : null_Float; // Float + Float -> OK Float npe = false? f : null_Float; // float + Float -> NPE 

因此,NPE是我们试图做如下事情的时候:

 Float npe = false? 1.0f : (Float)null; 

不pipe是不是,那就是问题。 🙂

编辑:实际上,仔细看来,这种情况下,实际上是哈姆雷特 (三元运算符和包装的整数types)和猫王 (自动拆箱null)益智游戏之间的混合。 无论如何,我只能推荐看video,这是非常教育和愉快的。

它看起来像JVM试图解开第二个空来浮动,而不是浮动 ,因此NullPointerException。 自己打一次。 我的意思是,第二个如果这样做,因为第一个真正的部分, 如果评估为浮动,而不是浮动。

经过一段时间的思考之后,我认为这是一种Java的方式告诉你,你正在做一些奇怪的事情。 只要不巢三元如果,你会没事的:-)