三元表达式有时会绕过编译器检查验证

Max*_*lis 21 java eclipse expression compilation ternary-operator

出于某种原因,以下代码正常编译:

public double getSomeDouble() {
    return "" != null ? 3.7d : null;
}
Run Code Online (Sandbox Code Playgroud)

我通常希望Eclipse将其标记为错误(null不能转换为double原语).

只是为了支持我的假设,这段代码不起作用:

public double getSomeDouble() {
    return null;
}
Run Code Online (Sandbox Code Playgroud)

Eclipse会将该return null行标记为错误,说明:

类型不匹配:无法转换nulldouble

为什么它在前面的代码片段中没有说同样的东西?!

T.J*_*der 23

这是由于自动装箱autounboxing.如果你看一下字节码(下面),你可以看到对Double.valueOf(装箱3.7d)和Double#doubleValue(取消装箱条件表达式的结果)的调用.条件运算符的操作数必须是相同的类型,因此编译器有效地将您的代码转换为:

public double getSomeDouble() {
    return ("" != null ? Double.valueOf(3.7d) : null).doubleValue();
}
Run Code Online (Sandbox Code Playgroud)

...因为Double是最具体常见的类型也可以找到3.7dnull.

我使用了一个字符串参数(以消除围绕不变表达式的编译器优化"" != null,编译器能够告诉它永远不会是真的):

public double getSomeDouble(String str) {
     return str != null ? 3.7d : null;
}
Run Code Online (Sandbox Code Playgroud)

实际上变成了:

public double getSomeDouble(String str) {
    return (str != null ? Double.valueOf(3.7d) : null).doubleValue();
}
Run Code Online (Sandbox Code Playgroud)

......确实得到了在运行时的NPE,当我传入nullstr,当它试图调用doubleValue()null.

这是我getSomeDouble(String)(来自javap -c MyClass)的字节码:

  public double getSomeDouble(java.lang.String);
    Code:
       0: aload_1
       1: ifnull        13
       4: ldc2_w        #7                  // double 3.7d
       7: invokestatic  #9                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      10: goto          14
      13: aconst_null
      14: invokevirtual #10                 // Method java/lang/Double.doubleValue:()D
      17: dreturn

  • 虽然错误原因现在已经很清楚,但我仍然想设置eclipse编译器来跟踪那些不适合我的(在运行时已经太晚了:()),你知道一种方法吗? (4认同)