Java条件运算符?:结果类型

时间:2010-04-10 23:53:54

标签: java nullpointerexception type-conversion conditional-operator

我对条件运算符有点疑惑。请考虑以下两行:

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

为什么f1变为null而第二个语句抛出NullPointerException?

Langspec-3.0 para 15.25 sais:

  

否则,第二和第三操作数分别为S1和S2类型。   设T1是应用到S1的拳击转换所产生的类型,并让   T2是将拳击转换应用于S2的结果。的类型   条件表达式是应用捕获转换的结果   (§5.1.10)至lub(T1,T2)(§15.12.2.7)。

因此对于false?1.0f:null T1是Float而T2是null类型。但是lub(T1,T2)的结果是什么?第15.12.2.7段只是有点太多了......

BTW,我在Windows上使用1.6.0_18。

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

5 个答案:

答案 0 :(得分:26)

不同之处在于编译时表达式的静态类型:

摘要

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

详细说明:

这是我从阅读the spec并从你得到的结果向后工作的理解。归结为f2 内部条件的第三个操作数的类型是null类型,而f2 外部条件的第三个操作数的类型被认为是Float。

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

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内部条件中的操作数类型是:

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

§15.25中的大多数规则都会被传递出来,并且确实应用了这个最终评估:

  

否则,第二和第三操作数分别为S1和S2类型。设T1是将拳击转换应用于S1所产生的类型,让T2为应用到S2的装箱转换所产生的类型。条件表达式的类型是将捕获转换(§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引用变量,表达式(null)的结果被成功赋值。

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

对于f2外部条件,类型为:

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

注意操作数类型与直接引用 null 文字的f1 / f2内部条件(§4.1)相比的差异。由于具有2个数字可转换类型的这种差异,§15.12.2.7中的此规则适用:

  
      
  • 否则,如果第二个和第三个操作数具有可转换(§5.1.8)到数字类型的类型,则有几种情况:...

         
        
    • 否则,二进制数字提升(§5.6.2)将应用于操作数类型,条件表达式的类型是第二个和第三个操作数的提升类型。请注意,二进制数字促销会执行取消装箱转化§5.1.8)和价值集转化(§5.1.13)。
    •   
  •   

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

答案 1 :(得分:2)

当您尝试将null分配给基元

时,以下内容将抛出NPE
    float f1 = false ? 1.0f: null;

我认为这是在第二个声明中导致NPE的原因。因为第一个三元组为float返回一个float,所以它也试图将false转换为float。

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

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

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

答案 2 :(得分:2)

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

    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;

答案 3 :(得分:2)

To be or not to be, that is the question.:)

编辑:实际上,仔细看看这个案例实际上是Hamlet(三元运算符和包裹的整数类型)和Elvis之间的混合(自动 - unboxing null)益智游戏。无论如何,我只能推荐观看视频,这是非常有教育意义的。

答案 4 :(得分:1)

看起来JVM试图将第二个空值解包到 float 而不是 Float ,因此NullPointerException。自己打一次。我的看法是第二个 if 这样做是因为第一个 true 部分,如果评估为float,而不是Float。

在再次考虑之后,我认为这是一种Java告诉你,你正在做一些奇怪的事情。只是不要嵌套三元ifs,你会没事的: - )