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

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

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

注意:即使使用JDK 8编译器(可能selectjava.lang.reflect.Executable作为常见types,而新的基类在旧JDK上不可用),也不会保留JDK <8兼容性的三元expression式。

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

当你考虑操作数的types时,问题就会变得更加明显:

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

具有types两种操作数的最专业的通用types,即this.methodthis.constructor共有的最专用的types。

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

在编译三元运算符时,Java 8编译器的一些(预发布)版本似乎在生成的代码中产生了对Executable的明确引用。 这会触发一个类加载,并因此在运行时使用类库<JDK 8运行时出现ClassNotFoundException ,因为Executable仅对于JDK≥8存在。

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

附加说明:可能会得出这样的结论:这个编译器错误在Java 8之前就存在了。然而,由OpenJDK 7生成的三进制字节代码与由OpenJDK 8生成的字节代码相同。事实上,expression式在运行时完全没有提到,代码实际上只是testing,分支,加载,返回而没有任何额外的检查正在进行。 所以放心,这不是一个问题(已经),似乎在Java 8的开发过程中似乎是一个暂时的问题。

这是在2013年5月3日,在官方JDK-8发布差不多一年之前提交的。 编译器在那个时候处于繁重的开发之中,所以会出现这样的兼容性问题。 我想,Spring团队只是testing了JDK-8构build,并试图解决问题,即使它们实际上是编译器问题。 通过JDK-8官方发布,这变得无关紧要。 现在这个代码中的三元运算符能够正常工作(在编译的.class文件中没有对Executable类的引用)。

目前在JDK-9中出现了类似的情况:JDK-8中可以很好编译的代码在JDK-9 javac中失败。 我想大多数这样的问题将会被解决,直到发布。

主要区别在于if else块是一个语句,而三元(在Java中更常被称为条件运算符)是一个expression式

语句可以做一些事情,例如在一些控制path上return给调用者。 一个expression式可以用在一个赋值中:

int n = condition ? 3 : 2;

所以在三态之后的两个expression式需要被强制为相同的types。 这可能会导致在Java中的一些奇怪的影响,特别是自动装箱和自动引用投射 – 这是您的发布代码中的评论所指的。 在你的情况下,expression式的强制是一个java.lang.reflect.Executabletypes(因为这是最专业的types ),并且在旧版本的Java中不存在。

在风格上,如果代码是类似于语句的,则应该使用if else块;如果类似于expression式,则使用三元。

当然,如果你使用lambda函数,你可以创build一个if else块。

三元expression式中的返回值types受到父类的影响,父类如Java 8中所述进行了更改。

很难看到为什么一个演员不能写。