Java 三元运算符与 <JDK8 兼容性中的 if/else

Java ternary operator vs if/else in <JDK8 compatibility

最近在看Spring框架的源码。这里有一些我无法理解的内容:

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

此方法是 class org.springframework.core.MethodParameter 的成员。代码通俗易懂,注释难懂

NOTE: no ternary expression to retain JDK <8 compatibility even when using the JDK 8 compiler (potentially selecting java.lang.reflect.Executable as common type, with that new base class not available on older JDKs)

在此上下文中使用三元表达式和使用 if...else... 构造有什么区别?

主要区别在于 if else 块是 语句 而三元(通常称为 条件语句 operator in Java) 是一个 expression.

一个语句可以对某些控制路径上的调用者做类似return的事情。 表达式可用于赋值:

int n = condition ? 3 : 2;

所以条件后的三元表达式中的两个表达式需要强制转换为同一类型。这可能会在 Java 中造成一些奇怪的影响,尤其是在自动装箱和自动引用转换方面——这就是您发布的代码中的注释所指的内容。在您的情况下,表达式的强制转换为 java.lang.reflect.Executable 类型(因为这是 最专业的类型 ),并且在 Java 的旧版本中不存在.

从风格上讲,如果代码是类似语句的,则应使用 if else 块,如果是表达式,则应使用三元代码块。

当然,如果您使用 lambda 函数,您可以使 if else 块的行为类似于表达式。

三元表达式中的 return 值类型受父 类 的影响,其更改如 Java 8.

中所述

很难理解为什么不能写演员表。

当您考虑操作数的类型时,问题变得更加明显:

this.method != null ? this.method : this.constructor

具有两个操作数的最特殊的通用类型,即 this.methodthis.constructor 最通用的通用类型。

在 Java 7 中,这是 java.lang.reflect.Member, however the Java 8 class library introduces a new type java.lang.reflect.Executable,它比一般的 Member 更专业。因此,对于 Java 8 class 库,三元表达式的结果类型是 Executable 而不是 Member.

Java 8 编译器的某些(预发布)版本似乎在编译三元运算符时在生成的代码中产生了对 Executable 的显式引用。这将触发 class 加载,因此当 运行 a class 库 < JDK 8 时,在运行时又会触发 ClassNotFoundException,因为 Executable仅存在 JDK ≥ 8.

正如 Tagir Valeev 在 中指出的那样,这实际上是 JDK 8 预发布版本中的一个错误,此后已修复,因此 if-else 解决方法和解释性评论现已过时。

补充说明: 人们可能会得出结论,这个编译器错误在 Java 8 之前就存在了。然而,由 OpenJDK 7 和OpenJDK 8生成的字节码是一样的。实际上,表达式的类型在运行时完全没有提及,代码真的只有test, branch, load, return 没有进行任何额外的检查。所以请放心,这不再是问题,而且确实似乎是 Java 8.

开发过程中的一个临时问题

这是在 2013 年 5 月 3 日 quite old commit 中引入的,几乎比官方 JDK-8 发布早一年。当时编译器正在大力开发,因此可能会出现此类兼容性问题。我想,Spring 团队刚刚测试了 JDK-8 构建并试图修复问题,即使它们实际上是编译器问题。在 JDK-8 官方发布时,这变得无关紧要了。现在这段代码中的三元运算符按预期工作正常(不存在编译的 .class 文件中对 Executable class 的引用)。

目前在JDK-9中出现了类似的事情:一些可以在JDK-8中很好地编译的代码在JDK-9 javac中失败了。我想,大多数此类问题将在发布之前得到解决。