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

Posted

技术标签:

【中文标题】<JDK8 兼容性中的 Java 三元运算符与 if/else【英文标题】:Java ternary operator vs if/else in <JDK8 compatibility 【发布时间】:2015-12-05 03:55:54 【问题描述】:

最近在看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 的成员。代码通俗易懂,cmets难。

注意:即使使用 JDK 8 编译器(可能选择 java.lang.reflect.Executable 作为通用类型,旧 JDK 上不提供该新基类),也没有保持 JDK

在这种情况下使用三元表达式和使用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,它比通用 Member 更专业。因此,对于 Java 8 类库,三元表达式的结果类型是 Executable 而不是 Member

在编译三元运算符时,Java 8 编译器的某些(预发布)版本似乎在生成的代码中产生了对 Executable 的显式引用。这将触发类加载,因此在使用 ClassNotFoundException,因为 Executable 仅适用于 JDK ≥ 8。

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

补充说明: 可能会得出这样的结论:Java 8 之前就存在这个编译器错误。但是,OpenJDK 7 生成的三进制字节码与由 OpenJDK 7 生成的字节码相同。 OpenJDK 8。实际上,表达式的类型在运行时完全没有提及,代码实际上只是测试、分支、加载、返回,没有任何额外的检查。所以请放心,这不再是一个问题,而且确实似乎是 Java 8 开发过程中的一个临时问题。

【讨论】:

那么用JDK 1.8编译的代码如何在JDK 1.7上运行。我知道用低版本JDK编译的代码可以在高版本JDK上运行而没有问题。反之亦然? @jddxf 只要您指定了正确的类文件版本并且不使用更高版本中不可用的任何功能,一切都很好。问题是肯定会发生的,但是如果这种使用隐含地发生在这种情况下。 @jddxf,使用 -source/-target javac 选项 谢谢大家,特别是dhke和Tagir Valeev,他们给出了详尽的解释【参考方案2】:

这是在 2013 年 5 月 3 日的 quite old commit 中引入的,比 JDK-8 正式发布早了将近一年。那个时候编译器正在大力开发,所以可能会出现这样的兼容性问题。我猜,Spring 团队刚刚测试了 JDK-8 构建并尝试修复问题,即使它们实际上是编译器问题。到了 JDK-8 官方版本,这变得无关紧要了。现在,此代码中的三元运算符可以正常工作(编译的 .class 文件中不存在对 Executable 类的引用)。

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

【讨论】:

+1。那么,这是早期编译器中的错误吗?提到Executable 的这种行为是否违反了规范的某些方面?还是只是 Oracle 意识到他们可以以仍然符合规范且不破坏向后兼容性的方式改变这种行为? @ruakh,我猜这是错误。在字节码中(在 Java-8 或之前的版本中)完全没有必要在两者之间显式转换为 Executable 类型。在 Java-8 中,表达式类型推断的概念发生了翻天覆地的变化,这部分被完全重写,因此早期实现出现错误也就不足为奇了。【参考方案3】:

主要区别在于ifelse 块是一个语句,而三元(在Java 中通常称为条件 运算符)是一个表达式。

语句可以在某些控制路径上对调用者执行return 之类的操作。 表达式可用于赋值:

int n = condition ? 3 : 2;

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

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

当然,如果您使用 lambda 函数,您可以使 if else 块表现得像一个表达式。

【讨论】:

【参考方案4】:

三元表达式中的返回值类型受父类影响,如 Java 8 中所述更改。

很难理解为什么无法编写演员表。

【讨论】:

以上是关于<JDK8 兼容性中的 Java 三元运算符与 if/else的主要内容,如果未能解决你的问题,请参考以下文章

java java.java中的三元if else运算符

java java.java中的三元if else运算符

java java.java中的三元if else运算符

java java.java中的三元if else运算符

java java.java中的三元if else运算符

java java.java中的三元if else运算符