java 布尔值、条件运算符和自动装箱
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3882095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Booleans, conditional operators and autoboxing
提问by BalusC
Why does this throw NullPointerException
为什么这会抛出 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;
}
while this doesn't
虽然这没有
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
?
The solution is by the way to replace false
by Boolean.FALSE
to avoid null
being unboxed to boolean
--which isn't possible. But that isn't the question. The question is why? Are there any references in JLS which confirms this behaviour, especially of the 2nd case?
解决方案是通过替换false
byBoolean.FALSE
以避免null
被拆箱 - 这boolean
是不可能的。但这不是问题。问题是为什么?JLS 中是否有任何参考资料证实了这种行为,尤其是第二种情况?
采纳答案by Bert F
The difference is that the explicit type of the returnsNull()
method affects the static typing of the expressions at compile time:
不同之处在于方法的显式类型returnsNull()
会影响编译时表达式的静态类型:
E1: `true ? returnsNull() : false` - boolean (auto-unboxing 2nd operand to boolean)
E2: `true ? null : false` - Boolean (autoboxing of 3rd operand to Boolean)
See Java Language Specification, section 15.25 Conditional Operator ? :
For E1, the types of the 2nd and 3rd operands are
Boolean
andboolean
respectively, so this clause applies:If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
Since the type of the expression is
boolean
, the 2nd operand must be coerced toboolean
. The compiler inserts auto-unboxing code to the 2nd operand (return value ofreturnsNull()
) to make it typeboolean
. This of course causes the NPE from thenull
returned at run-time.For E2, types of the 2nd and 3rd operands are
<special null type>
(notBoolean
as in E1!) andboolean
respectively, so no specific typing clause applies (go read 'em!), so the final "otherwise" clause applies:Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
- S1 ==
<special null type>
(see §4.1) - S2 ==
boolean
- T1 == box(S1) ==
<special null type>
(see last item in list of boxing conversions in §5.1.7) - T2 == box(S2) == `Boolean
- lub(T1, T2) ==
Boolean
So the type of the conditional expression is
Boolean
and the 3rd operand must be coerced toBoolean
. The compiler inserts auto-boxing code for the 3rd operand (false
). The 2nd operand doesn't need the auto-unboxing as inE1
, so no auto-unboxing NPE whennull
is returned.- S1 ==
对于 E1,第 2 个和第 3 个操作数的类型分别是
Boolean
和boolean
,因此该条款适用:如果第二个和第三个操作数之一是布尔类型,另一个是布尔类型,则条件表达式的类型是布尔类型。
由于表达式的类型是
boolean
,因此必须将第二个操作数强制转换为boolean
。编译器将自动拆箱代码插入到第二个操作数( 的返回值returnsNull()
)以使其类型为boolean
。这当然会导致 NPE 从null
运行时返回。对于E2,类型的第二和第三个操作数是
<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 == box(S2) == `布尔值
- lub(T1, T2) ==
Boolean
所以条件表达式的类型是
Boolean
并且第三个操作数必须被强制转换为Boolean
。编译器为第三个操作数 (false
)插入自动装箱代码。第二个操作数不需要自动拆箱E1
,因此在null
返回时没有自动拆箱 NPE 。- S1 ==
This question needs a similar type analysis:
这个问题需要类似的类型分析:
回答by jjungnickel
The line:
线路:
Boolean b = true ? returnsNull() : false;
is internally transformed to:
内部转换为:
Boolean b = true ? returnsNull().booleanValue() : false;
to perform the unboxing; thus: null.booleanValue()
will yield a NPE
执行拆箱;因此:null.booleanValue()
将产生一个 NPE
This is one of the major pitfalls when using autoboxing. This behavior is indeed documented in 5.1.8 JLS
这是使用自动装箱时的主要缺陷之一。这种行为确实记录在5.1.8 JLS
Edit: I believe the unboxing is due to the third operator being of boolean type, like (implicit cast added):
编辑:我相信拆箱是由于第三个运算符是布尔类型的,例如(添加了隐式转换):
Boolean b = (Boolean) true ? true : false;
回答by axtavt
From Java Language Specification, section 15.25:
- If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
- 如果第二个和第三个操作数之一是布尔类型,另一个是布尔类型,则条件表达式的类型是布尔类型。
So, the first example tries to call Boolean.booleanValue()
in order to convert Boolean
to boolean
as per the first rule.
所以,第一个例子试图调用Boolean.booleanValue()
,以转化Boolean
到boolean
按第一条规则。
In the second case the first operand is of the null type, when the second is not of the reference type, so autoboxing conversion is applied:
在第二种情况下,第一个操作数是空类型,当第二个不是引用类型时,因此应用自动装箱转换:
- Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
- 否则,第二个和第三个操作数分别是 S1 和 S2 类型。设 T1 为对 S1 应用装箱转换后的类型,设 T2 为对 S2 应用装箱转换后的类型。条件表达式的类型是将捕获转换(第 5.1.10 节)应用于 lub(T1, T2)(第 15.12.2.7 节)的结果。
回答by Yanhui Zhou
We can see this problem from byte code. At line 3 of main's byte code, 3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
, the boxing Boolean of value null, invokevirtual
the method java.lang.Boolean.booleanValue
, it will throw NPE of course.
我们可以从字节码中看出这个问题。在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