布尔值,条件运算符和自动装箱

Bal*_*usC 128 java autoboxing boolean nullpointerexception conditional-operator

为什么这会抛出 NullPointerException

public static void main(String[] args) throws Exception {
    Boolean b = true ? returnsNull() : false; // NPE on this line.
    System.out.println(b);
}

public static Boolean returnsNull() {
    return null;
}
Run Code Online (Sandbox Code Playgroud)

虽然这不是

public static void main(String[] args) throws Exception {
    Boolean b = true ? null : false;
    System.out.println(b); // null
}
Run Code Online (Sandbox Code Playgroud)

解决办法是更换的方式false通过Boolean.FALSE,以避免null被拆箱到boolean,可呈现是不可能的.但这不是问题.问题是为什么?JLS中是否有任何引用证实了这种行为,尤其是第二种情况?

Ber*_*t F 90

区别在于方法的显式类型returnsNull()会影响编译时表达式的静态类型:

E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)

E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
Run Code Online (Sandbox Code Playgroud)

请参阅Java语言规范,第15.25节" 条件运算符"?:

  • 对于E1,该类型的第二和第三个操作数是Booleanboolean分别,故该条款适用:

    如果第二个和第三个操作数之一是boolean类型,另一个类型是Boolean类型,那么条件表达式的类型是boolean.

    由于表达式的类型是boolean,第二个操作数必须被强制转换为boolean.编译器将自动拆箱代码插入第二个操作数(返回值returnsNull())以使其键入boolean.这当然会导致NPE null在运行时返回.

  • 对于E2,第2和第3个操作数的类型<special null type>(分别不是Boolean在E1!中)boolean,因此不适用特定的输入子句(去读它们!),因此最终的"其他"条款适用:

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

    • S1 == <special null type>(见§4.1)
    • S2 == boolean
    • T1 == box(S1)== <special null type>(参见§5.1.7中拳击转换列表中的最后一项 )
    • T2 ==方框(S2)==`布尔值
    • lub(T1,T2)== Boolean

    所以条件表达式的类型是Boolean,第三个操作数必须被强制转换Boolean.编译器为第3个操作数(false)插入自动装箱代码.第二个操作数不需要自动取消装箱E1,因此null返回时不会自动取消装箱NPE .


这个问题需要类似的类型分析:

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

  • 有道理......我想.[§15.12.2.7](http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#341287)很痛苦. (4认同)

jju*_*kel 25

这条线:

    Boolean b = true ? returnsNull() : false;
Run Code Online (Sandbox Code Playgroud)

内部转变为:

    Boolean b = true ? returnsNull().booleanValue() : false; 
Run Code Online (Sandbox Code Playgroud)

执行拆箱; 因此:null.booleanValue()将产生NPE

这是使用自动装箱时的主要缺陷之一.这种行为确实记录在5.1.8 JLS中

编辑:我认为拆箱是由于第三个运算符是布尔类型,如(隐式强制转换):

   Boolean b = (Boolean) true ? true : false; 
Run Code Online (Sandbox Code Playgroud)

  • 当最终值是一个布尔对象时,为什么它会尝试这样拆箱? (2认同)

axt*_*avt 16

Java语言规范,第15.25节:

  • 如果第二个和第三个操作数之一是boolean类型,另一个类型是Boolean类型,那么条件表达式的类型是boolean.

所以,第一个例子试图调用Boolean.booleanValue(),以转化Booleanboolean按第一条规则.

在第二种情况下,第一个操作数是null类型,当第二个操作数不是引用类型时,应用自动装箱转换:

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