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

2022-08-31 09:27:13

最近我正在阅读Spring Framework的源代码。我无法理解的东西在这里:

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;
    }
}

此方法是类 的成员。代码很容易理解,而注释很难。org.springframework.core.MethodParameter

注意:即使使用 JDK 8 编译器,也没有三元表达式来保持 JDK <8 兼容性(可能选择为通用类型,而该新基类在较旧的 JDK 上不可用)java.lang.reflect.Executable

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


答案 1

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

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

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

在Java 7中,这是java.lang.reflect.Member,但是Java 8类库引入了一种新类型java.lang.reflect.Executable,它比泛型更专业。因此,对于 Java 8 类库,三元表达式的结果类型是 而不是 。MemberExecutableMember

Java 8 编译器的某些(预发布)版本在编译三元运算符时,似乎对内部生成的代码进行了显式引用。这将触发类加载,因此当使用< JDK 8 的类库运行时,这将触发类加载,因为仅对 JDK ≥ 8 存在。ExecutableClassNotFoundExceptionExecutable

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

附加说明:人们可能会得出这样的结论:这个编译器错误在Java 8之前就已经存在了。但是,OpenJDK 7 为三元生成的字节码与 OpenJDK 8 生成的字节码相同。事实上,表达式的类型在运行时完全没有被提及,代码实际上只是测试,分支,加载,返回,没有任何额外的检查。因此,请放心,这不再是问题,而且在Java 8开发过程中确实是一个暂时的问题。


答案 2

这是在2013年5月3日相当古老的提交中引入的,比官方JDK-8发布早了将近一年。当时编译器正在进行大量开发,因此可能会出现此类兼容性问题。我猜,Spring团队刚刚测试了JDK-8版本并试图修复问题,即使它们实际上是编译器问题。到JDK-8正式发布时,这变得无关紧要。现在,此代码中的三元运算符可以按预期工作正常(不存在对已编译.class文件中的类的引用)。Executable

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