Java三元运算符与<JDK8兼容性中的if / else
最近我正在阅读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编译器(可能选择java.lang.reflect.Executable
作为常见类型,而新的基类在旧JDK上不可用),也不会保留JDK <8兼容性的三元表达式。
在这种情况下使用三元表达式和使用if...else...
构造之间有什么区别?
当你考虑操作数的类型时,问题就会变得更加明显:
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
。 这会触发类加载,并因此在运行时使用类库<JDK 8运行时ClassNotFoundException
,因为Executable
仅在JDK≥8时存在。
正如Tagir Valeev在回答中所指出的那样,这实际上是JDK 8的预发布版本中的一个错误,并且已经被修复,所以if-else
解决方法和解释性评论现在都已过时。
补充说明:可能会得出这样的结论:该编译器错误在Java 8之前出现。但是,由OpenJDK 7为三元生成的字节代码与由OpenJDK 8生成的字节代码相同。事实上,表达式在运行时完全没有提到,代码实际上只是测试,分支,加载,返回而没有进行任何额外的检查。 因此,请放心,这不是一个问题(现在已经),而且在Java 8开发过程中似乎确实是一个暂时的问题。
这是在2013年5月3日相当古老的版本中提出的,距JDK-8正式发布差不多一年。 编译器在那个时候处于繁重的开发中,所以可能会出现这种兼容性问题。 我想,Spring团队只是测试了JDK-8构建,并试图解决问题,即使它们实际上是编译器问题。 通过JDK-8官方发布,这变得无关紧要。 现在,此代码中的三元运算符按预期正常工作(不存在对已编译的.class-file中的Executable
类的引用)。
目前在JDK-9中出现类似的情况:JDK-8中可以很好编译的代码在JDK-9 javac中失败。 我想,这些问题大部分都会在发布之前得到修复。
主要区别在于if
else
块是一个语句,而三元(更常称为Java中的条件运算符)是一个表达式。
一条语句可以做一些事情,例如在一些控制路径上return
给调用者。 表达式可以用于赋值中:
int n = condition ? 3 : 2;
因此,条件之后的三元表达式需要被强制为相同的类型。 这可能会在Java中引起一些奇怪的效果,特别是在自动装箱和自动引用投射方面 - 这就是您的发布代码中的评论所指的。 在你的情况下,表达式的强制转化为java.lang.reflect.Executable
类型(因为这是最专业化的类型),并且在旧版本的Java中不存在。
在风格上,如果代码类似于语句,则应该使用if
else
块;如果类似表达式,则使用三元。
当然,如果你使用lambda函数,你可以使if
else
块的行为像表达式一样。
上一篇: Java ternary operator vs if/else in <JDK8 compatibility