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


113

最近,我正在阅读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;
    }
}

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

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

if...else...在这种情况下,使用三元表达式和使用构造之间有什么区别?

Answers:


103

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

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

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

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

Java 8编译器的某些(预发行版)Executable在编译三元运算符时似乎已经对内部生成的代码产生了明确的引用。这将触发类加载,从而在ClassNotFoundException使用<JDK 8类库运行时又会在运行时触发a ,因为Executable仅对于JDK≥8存在。

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

补充说明:可能得出这样的结论:该编译器错误在Java 8之前存在。但是,OpenJDK 7为三元生成的字节代码与OpenJDK 8生成的字节代码相同。表达式在运行时完全没有提及,代码实际上仅是测试,分支,加载,返回而无需进行任何其他检查。因此请放心,这不再是问题,在Java 8开发过程中确实确实是暂时的问题。


1
然后,如何在JDK 1.7上运行用JDK 1.8编译的代码。我知道用较低版本的JDK编译的代码可以在较高版本的JDK上运行而不会遇到麻烦。反之亦然?
jddxf

1
@jddxf只要您指定了正确的类文件版本并且不使用更高版本中不提供的任何功能,一切都很好。问题肯定会发生,但是,如果像这种情况那样隐式发生这种使用,就会发生问题。
dhke 2015年

13
@jddxf,使用-source / -target javac选项
Tagir Valeev 2015年

1
谢谢所有人,尤其是dhke和Tagir Valeev,他们给出了详尽的解释
jddxf

30

这是在2013年5月3日的相当老的提交中引入的,比JDK-8正式发布快一年了。那个时候,编译器正在繁重的开发中,因此可能会出现这种兼容性问题。我猜想,Spring团队只是测试了JDK-8构建并试图解决问题,即使它们实际上是编译器问题。通过JDK-8正式发布,这变得无关紧要。现在,此代码中的三元运算符可以按预期工作(没有对已Executable编译.class文件中的class的引用)。

当前在JDK-9中出现了类似的情况:JDK-9 javac无法在JDK-8中很好地编译某些代码。我想,大多数此类问题将在发布之前得到解决。


2
+1。那么,这是早期编译器中的错误吗?它所指的行为是否Executable违反了规范的某些方面?还是仅仅是Oracle意识到他们可以以仍然符合规范且不破坏向后兼容性的方式更改此行为?
ruakh 2015年

2
@ruakh,我想那是错误。在字节码中(在Java-8或更低版本中),完全不需要显式转换Executable为中间类型。在Java-8中,表达式类型推断的概念发生了巨大变化,并且此部分已被完全重写,因此早期的实现中有bug并不奇怪。
Tagir Valeev 2015年

7

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

可以做这样的事情return对某些控制路径的调用者。的表达可以在分配中使用:

int n = condition ? 3 : 2;

因此,条件之后的三元组中的两个表达式必须强制为同一类型。这可能会在Java中引起一些奇怪的效果,尤其是在自动装箱和自动引用转换方面-这就是您发布的代码中的注释所指的内容。在您的情况下,表达式的强制将是一个java.lang.reflect.Executable类型(因为这是最专门的类型),并且在较早的Java版本中不存在。

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

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


6

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.