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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 03:49:54  来源:igfitidea点击:

Booleans, conditional operators and autoboxing

javanullpointerexceptionbooleanconditional-operatorautoboxing

提问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 falseby Boolean.FALSEto avoid nullbeing 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?

解决方案是通过替换falsebyBoolean.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 ? :

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

  • For E1, the types of the 2nd and 3rd operands are Booleanand booleanrespectively, 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 to boolean. The compiler inserts auto-unboxing code to the 2nd operand (return value of returnsNull()) to make it type boolean. This of course causes the NPE from the nullreturned at run-time.

  • For E2, types of the 2nd and 3rd operands are <special null type>(not Booleanas in E1!) and booleanrespectively, 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 Booleanand the 3rd operand must be coerced to Boolean. The compiler inserts auto-boxing code for the 3rd operand (false). The 2nd operand doesn't need the auto-unboxing as in E1, so no auto-unboxing NPE when nullis returned.

  • 对于 E1,第 2 个和第 3 个操作数的类型分别是Booleanboolean,因此该条款适用:

    如果第二个和第三个操作数之一是布尔类型,另一个是布尔类型,则条件表达式的类型是布尔类型。

    由于表达式的类型是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 。



This question needs a similar type analysis:

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

Java conditional operator ?: result type

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

回答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:

来自Java 语言规范,第 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 Booleanto booleanas per the first rule.

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

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, invokevirtualthe 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