【发布时间】:2021-08-15 12:47:09
【问题描述】:
Java 8 引入了重要的新语言特性,例如 lambda 表达式。
语言中的这些变化是否伴随着编译字节码的重大变化,导致它无法在不使用某些逆向翻译器的情况下在 Java 7 虚拟机上运行?
【问题讨论】:
标签: java jvm compatibility java-7 java-8
Java 8 引入了重要的新语言特性,例如 lambda 表达式。
语言中的这些变化是否伴随着编译字节码的重大变化,导致它无法在不使用某些逆向翻译器的情况下在 Java 7 虚拟机上运行?
【问题讨论】:
标签: java jvm compatibility java-7 java-8
据我所知,JDK 8 中的这些更改都不需要添加新的字节码。部分 lambda 检测是使用 invokeDynamic(JDK 7 中已经存在)完成的。因此,从 JVM 指令集的角度来看,没有什么可以使代码库不兼容。不过,有很多相关的 API 和编译器改进可能会使来自 JDK 8 的代码难以在以前的 JDK 下编译/运行(但我没有尝试过)。
也许以下参考资料可以帮助加深对如何检测与 lambda 相关的更改的理解。
这些详细解释了事物是如何在引擎盖下进行检测的。也许您可以在那里找到问题的答案。
【讨论】:
class C extends A with B,通过普通接口A 和B 以及伴随类A$class 和B$class 实现。 C 类只是将方法转发给静态伴随类。自类型根本不被强制执行,lambdas 在编译时被转换为抽象内部类,new D with A with B 表达式也是如此。模式匹配是一堆 if-else 结构。非本地退货?来自 lambda 的 try-catch 机制。还剩什么吗? (有趣的是,我的 scalac 说 1.6 是默认值)
不,在源代码中使用 1.8 功能需要您以 1.8 虚拟机为目标。我刚刚尝试了新的 Java 8 版本并尝试使用-target 1.7 -source 1.8 进行编译,编译器拒绝:
$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8
【讨论】:
如果您愿意使用“retrotranslator”,请尝试 Esko Luontola 出色的 Retrolambda:https://github.com/orfjackal/retrolambda
【讨论】:
默认方法需要对字节码和 JVM 进行此类更改,而这在 Java 7 上是不可能做到的。Java 7 及更低版本的字节码验证器将拒绝与方法主体的接口(静态初始化方法除外)。尝试在调用方使用静态方法模拟默认方法不会产生相同的结果,因为可以在子类中覆盖默认方法。 Retrolambda 对向后移植默认方法的支持有限,但它永远无法完全向后移植,因为它确实需要新的 JVM 功能。
Lambdas 可以在 Java 7 上按原样运行,如果必要的 API 类在那里存在的话。 invokedynamic 指令存在于 Java 7 中,但可以实现 lambdas,以便它在编译时生成 lambda 类(早期的 JDK 8 构建就是这样做的),在这种情况下,它可以在任何 Java 版本上工作。 (Oracle 决定为 lambdas 使用 invokedynamic 以备将来验证;也许有一天 JVM 将拥有一流的函数,因此可以将 invokedynamic 更改为使用它们,而不是为每个 lambda 生成一个类,从而提高性能。)Retrolambda 所做的是它处理所有这些调用动态指令并用匿名类替换它们;与第一次调用 lamdba invokedynamic 时 Java 8 在运行时所做的相同。
Repeating Annotations 只是语法糖。它们是与以前版本兼容的字节码。在 Java 7 中,您只需要自己实现帮助方法(例如 getAnnotationsByType),它隐藏包含重复注释的容器注释的实现细节。
AFAIK,Type Annotations 仅在编译时存在,因此它们不需要更改字节码,因此只需更改 Java 8 编译类的字节码版本号就足以使它们在 Java 7 上工作。
Method parameter names 存在于 Java 7 的字节码中,因此也兼容。您可以通过读取方法的字节码并查看方法的调试信息中的局部变量名称来访问它们。例如,Spring Framework 正是这样做来实现@PathVariable,所以可能有一个您可以调用的库方法。因为抽象接口方法没有方法体,所以 Java 7 中的接口方法不存在调试信息,Java 8 中也不存在 AFAIK。
The other new features 主要是新的 API、对 HotSpot 和工具的改进。一些新的 API 可作为 3rd 方库使用(例如 ThreeTen-Backport 和 streamsupport)。
总结,默认方法需要新的 JVM 特性,但其他语言特性不需要。如果要使用它们,则需要在 Java 8 中编译代码,然后将带有 Retrolambda 的字节码转换为 Java 5/6/7 格式。至少需要更改字节码版本,并且 javac 不允许 -source 1.8 -target 1.7 因此需要逆向翻译器。
【讨论】:
你可以做-source 1.7 -target 1.7 然后它会编译。但是如果你有 java 8 特定的特性,比如 lambdas,它就不会编译
【讨论】:
-source 1.7 不会飞。