一、引言
二、背景知识
条件运算符有3个操作数表达式。"?"出现在第一个和第二个表达式之间,而":" 出现在第二个和第三个表达式之间。
注意,第一个表达式必须是boolean或Boolean类型,否则会产生编译错误。而且让第二个或第三个操作数表达式调用一个void方法也会产生编译错误。
条件表达式的类型按照如下规则来确定:
• 如果第二个和第三个操作数具有相同的类型(该类型可能是一个空类型),那么它就是条件表达式的类型。
• 如果第二个和第三个操作数中的一个是基本类型T,另一个是对T进行装箱的结果类型,那么条件表达式的类型就是T。
• 如果第二个和第三个操作数中的一个是空类型,另一个是引用类型,那么条件表达式的类型就是该引用类型。
• 否则,如果第二个和第三个操作数中有可转换为数值类型的类型,那么就有这几种情形:
♦若操作数之一是byte或Byte类型,另一个是short或Short类型,那么条件表达式的类型就是short。
♦若操作数之一是T类型,其中T是byte、short或char,另一个操作数是int类型的常量表达式,其值可用类型T表示,那么条件表达式的类型是T。
♦若操作数之一是T类型,其中T是Byte、Short或Character,另一个操作数是为int类型的常量表达式,其值可用类型U表示,U是对T进行拆箱的结果,那么条件表达式的类型是U。
♦否则,二元数值提升(binary numeric promotion)会被应用于操作数类型,并且条件表达式的类型是第二个和第三个操作数提升后的类型。注意,二元数值提升执行值集转换且可能执行拆箱转换 。
• 否则,第二个和第三个操作数的类型分别是S1和S2。令T1是对S1进行装箱转换的结果类型,T2是对S2进行装箱转换的结果类型 。条件表达式的类型就是对lub(T1, T2)进行捕获转换的结果。
在运行期,条件表达式的第一个操作数表达式首先进行计算。如有必要,会在结果上执行拆箝转换。随后计算所选择的操作数表达式,并且将结果转换为按上面指定规则所确定的条件表达式的类型。这个转换可能包括装箱或拆箱转换。
声明:以上内容是本人翻译自《The Java® Language Specification Java SE 7 Edition》2013-02-28这一版并截取了其中的一部分。
三、回归最开始的问题
Float localFloat1 = (Float)null;
Float localFloat2 = Float.valueOf(((Float)null).floatValue());
下面来仔细分析一下:
(false ? 1.0f : null)这个表达式是第一个和第二个语句都有的,该表达式中第二个操作数的类型是float、第三个操作数的类型是null,所以可以应用下面这条规则:
否则,第二个和第三个操作数的类型分别是S1和S2。令T1是对S1进行装箱转换的结果类型,T2是对S2进行装箱转换的结果类型 。条件表达式的类型就是对lub(T1, T2)进行捕获转换的结果。所以: S1 = float
S2 = null
T1 = Float
T2 = null
那么条件表达式(false ? 1.0f : null)的类型 = Float
对于f1, 表达式的结果null成功赋值给一个Float引用变量。
对于f2,现在第二个操作数的类型为float,第三个操作数的类型是Float(参见上述分析),所以接下来可对其应用如下规则:
如果第二个和第三个操作数中的一个是基本类型T,另一个是对T进行装箱的结果类型,那么条件表达式的类型就是T。
所以f2最终的类型为float,则先要对选择的第三个操作数(null)执行拆箱操作,然后再执行装箱操作,在拆箱过程中产生NullPointerException。
四、总结
混合类型的计算会引起混乱,在条件运算符?:中表现的尤为明显,所以建议在条件表达式中保持第二个和第三个操作数的类型相同。
五、参考资料
《The Java® Language Specification Java SE 7 Edition》
http://stackoverflow.com/questions/2615498/java-conditional-operator-result-type