java - 布尔值,条件运算符和自动装箱
为什么抛出false
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;
}
虽然这不是
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
顺便提一下,该解决方案是通过2377135880046604321替换false,以避免null被拆箱至boolean - 这是不可能的。 但这不是问题。 问题是为什么? JLS中是否有任何引用证实了这种行为,尤其是第二种情况?
4个解决方案
89 votes
不同之处在于Boolean方法的显式类型会影响编译时表达式的静态类型:
E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)
E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
请参阅Java语言规范,第15.25节“条件运算符”?:
对于E1,第2和第3个操作数的类型分别为Boolean和Boolean,因此本节适用于:
如果第二个和第三个操作数之一是boolean类型,另一个类型是Boolean类型,那么条件表达式的类型是boolean。
由于表达式的类型是Boolean,因此必须将第二个操作数强制转换为Boolean.编译器将自动取消装箱代码插入第二个操作数(返回值false),使其类型为E1.这当然会导致来自null的NPE 在运行时返回。
对于E2,第2和第3个操作数的类型分别为Boolean(不是像E1中的Boolean)和false,因此不适用特定的输入条款(请阅读'em!),因此最终的“其他”条款适用:
否则,第二和第三操作数分别是S1和S2类型。 设T1是将拳击转换应用于S1所产生的类型,让T2为应用到S2的装箱转换所产生的类型。 条件表达式的类型是将捕获转换(第5.1.10节)应用于lub(T1,T2)(第15.12.2.7节)的结果。
S1 == Boolean(见§4.1)
S2 == Boolean
T1 == box(S1)== Boolean(参见§5.1.7中的拳击转换列表中的最后一项)
T2 ==方框(S2)==`布尔值
lub(T1,T2)== Boolean
因此条件表达式的类型为Boolean,第3个操作数必须强制转换为Boolean.编译器为第3个操作数(false)插入自动装箱代码。 第二个操作数不需要E1中的自动拆箱,因此返回null时不会自动取消装箱NPE。
这个问题需要类似的类型分析:
Java条件运算符?:结果类型
Bert F answered 2019-06-04T05:30:03Z
22 votes
这条线:
Boolean b = true ? returnsNull() : false;
内部转变为:
Boolean b = true ? returnsNull().getBoolean() : false;
执行拆箱; 因此:null.getBoolean()将产生NPE
这是使用自动装箱时的主要缺陷之一。 这种行为确实记录在5.1.8 JLS中
编辑:我认为拆箱是由于第三个运算符是布尔类型,如(隐式强制转换):
Boolean b = (Boolean) true ? true : false;
jjungnickel answered 2019-06-04T05:30:59Z
16 votes
从Java语言规范,第15.25节:
如果是第二个和第三个之一 操作数的类型为boolean和 另一种类型是布尔型, 那么条件的类型 表达式是布尔值。
因此,第一个示例尝试调用Boolean.booleanValue(),以便根据第一个规则将Boolean转换为boolean。
在第二种情况下,第一个操作数是null类型,当第二个操作数不是引用类型时,应用自动装箱转换:
否则,第二和第三 操作数是S1和S2类型 分别。 设T1是那种类型 应用拳击的结果 转换为S1,让T2成为 应用拳击造成的类型 转换为S2。 的类型 条件表达式就是结果 应用捕获转换 (§5.1.10)至lub(T1,T2)(§15.12.2.7)。
axtavt answered 2019-06-04T05:31:59Z
0 votes
我们可以从字节代码中看到这个问题。 在main的字节码的第3行,3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z,值为null的装箱布尔值,invokevirtual方法java.lang.Boolean.booleanValue,它当然会抛出NPE。
public static void main(java.lang.String[]) throws java.lang.Exception;
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: invokestatic #2 // Method returnsNull:()Ljava/lang/Boolean;
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
6: invokestatic #4 // Method java/lang/Boolean.valueOf:(Z)Ljava/lang/Boolean;
9: astore_1
10: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
13: aload_1
14: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
17: return
LineNumberTable:
line 3: 0
line 4: 10
line 5: 17
Exceptions:
throws java.lang.Exception
public static java.lang.Boolean returnsNull();
descriptor: ()Ljava/lang/Boolean;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: aconst_null
1: areturn
LineNumberTable:
line 8: 0
Yanhui Zhou answered 2019-06-04T05:32:31Z