布尔值、条件运算符和自动装箱
为什么这会抛出 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;
}
而这却不会
public static void main(String[] args) throws Exception {
Boolean b = true ? null : false;
System.out.println(b); // null
}
?
解决方案是顺便将 false
替换为 Boolean.FALSE
以避免 null
被拆箱为 boolean
--这是不可能的。但这不是问题。问题是为什么? JLS 中是否有任何参考文献证实了这种行为,尤其是第二种情况?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
区别在于
returnsNull()
方法的显式类型会影响编译时表达式的静态类型:请参阅 Java 语言规范,15.25 条件运算符 ? :
对于E1,第二个和第三个操作数的类型分别是
Boolean
和boolean
,因此该子句适用:<块引用>
如果第二个和第三个操作数之一是布尔类型,另一个是布尔类型,则条件表达式的类型是布尔类型。
由于表达式的类型为
boolean
,因此第二个操作数必须强制为boolean
。编译器将自动拆箱代码插入到第二个操作数(returnsNull()
的返回值),使其类型为boolean
。这当然会导致运行时返回null
的 NPE。对于E2,第二个和第三个操作数的类型是
<特殊空类型>
(不是E1中的Boolean
!)和boolean
分别,因此不适用特定的类型子句 (去读他们!),所以最后的“否则”子句适用:<块引用>
否则,第二个和第三个操作数的类型分别为 S1 和 S2。令 T1 为对 S1 应用装箱转换所产生的类型,并令 T2 为对 S2 应用装箱转换所产生的类型。条件表达式的类型是将捕获转换 (§5.1.10) 应用于 lub(T1, T2) (§15.12.2.7) 的结果。
<特殊空类型>
(请参阅§4.1)布尔值
<特殊空类型>
(请参阅 §5.1.7)布尔值
因此,条件表达式的类型为
Boolean
,并且第三个操作数必须强制为Boolean
。编译器为第三个操作数插入自动装箱代码 (false
)。第二个操作数不需要像E1
中那样自动拆箱,因此当返回null
时不会自动拆箱 NPE。这个问题需要类似的类型分析:
Java条件运算符?:结果类型
The difference is that the explicit type of the
returnsNull()
method affects the static typing of the expressions at compile time: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: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:<special null type>
(see §4.1)boolean
<special null type>
(see last item in list of boxing conversions in §5.1.7)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.This question needs a similar type analysis:
Java conditional operator ?: result type
该行:
在内部转换为:
执行拆箱;因此:
null.booleanValue()
将产生一个 NPE这是使用自动装箱时的主要陷阱之一。此行为确实记录在 5.1.8 JLS< /a>
编辑:我相信拆箱是由于第三个运算符是布尔类型,例如(添加了隐式转换):
The line:
is internally transformed to:
to perform the unboxing; thus:
null.booleanValue()
will yield a NPEThis is one of the major pitfalls when using autoboxing. This behavior is indeed documented in 5.1.8 JLS
Edit: I believe the unboxing is due to the third operator being of boolean type, like (implicit cast added):
来自 Java 语言规范第 15.25 节:
因此,第一个示例尝试调用
Boolean.booleanValue()
以便根据第一条规则将Boolean
转换为boolean
。在第二种情况下,第一个操作数为空类型,而第二个操作数不是引用类型,因此应用自动装箱转换:
From Java Language Specification, section 15.25:
So, the first example tries to call
Boolean.booleanValue()
in order to convertBoolean
toboolean
as per the first rule.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:
我们从字节码就可以看出这个问题。 main 字节码第 3 行,
3: invokevirtual #3 // Method java/lang/Boolean.booleanValue:()Z
,装箱布尔值为 null,invokevirtual
方法java.lang.Boolean.booleanValue
,它当然会抛出NPE。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 methodjava.lang.Boolean.booleanValue
, it will throw NPE of course.