Java8代码可以编译成在 Java7JVM 上运行吗?

Java8引入了重要的新语言特性,比如 lambda 表达式。

语言中的这些变化是否伴随着编译后的字节码的重大变化,以至于在不使用翻译器的情况下就无法在 Java7虚拟机上运行?

72488 次浏览

据我所知,JDK 8中的这些更改都不需要添加新的字节码。Lambda 检测的一部分是使用 invokeDynamic完成的(它已经存在于 JDK7中)。因此,从 JVM 指令集的角度来看,没有什么可以使代码库不兼容。但是,有许多相关的 API 和编译器改进可能会使得 JDK 8的代码难以在以前的 JDK 下编译/运行(但是我还没有尝试过这种方法)。

也许下面的参考资料可以在某种程度上帮助丰富对如何检测与 lambda 相关的变化的理解。

这些详细解释了如何在引擎盖下的东西是仪器。也许你可以在那里找到你的问题的答案。

不,在源代码中使用1.8特性需要以1.8 VM 为目标。我刚刚尝试了新的 Java8版本,并尝试用 -target 1.7 -source 1.8进行编译,但编译器拒绝了:

$ javac Test -source 1.8 -target 1.7
javac: source release 1.8 requires target release 1.8

如果你愿意使用一个“逆向翻译”尝试埃斯科罗昂托拉的优秀逆向朗姆达: https://github.com/orfjackal/retrolambda

默认方法需要对字节码和 JVM 进行更改,这在 Java7上是不可能做到的。Java7及以下版本的字节码验证器将拒绝带有方法体的接口(静态初始化器方法除外)。尝试使用调用方的静态方法来模拟默认方法不会产生相同的结果,因为默认方法可以在子类中重写。逆行对回移默认方法的支持有限,但是它永远不能完全回移,因为它确实需要新的 JVM 特性。

如果需要的 API 类存在,Lambdas 可以在 Java7上原样运行。InvokedDynamic 指令存在于 Java 7中,但是可以实现 lambdas,以便在编译时生成 lambda 类(早期的 JDK 8构建就是这样做的) ,在这种情况下,它可以在任何 Java 版本上工作。(Oracle 决定在将来的验证中使用 lambdas 的 invokedDynamic; 也许有一天 JVM 会拥有一流的函数,那么 invokedDynamic 就可以更改为使用它们,而不是为每个 lambda 生成一个类,从而提高性能。)Retrolambda 所做的就是处理所有这些 invokedDynamic 指令,并用匿名类替换它们; 这与 Java 8在运行时第一次调用 lamdba invokedDynamic 时所做的相同。

重复注释 只是句法上的糖。它们与以前的版本是兼容的字节码。在 Java7中,您只需要实现自己的 helper 方法(例如 GetAnnotationsByType) ,它隐藏了包含重复注释的容器注释的实现细节。

AFAIK,类型注释只存在于编译时,因此它们不需要更改字节码,所以只需更改 Java 8编译类的字节码版本号就足以使它们在 Java 7上工作。

方法参数名 存在于 Java7的字节码中,因此也是兼容的。您可以通过读取方法的字节码并查看方法调试信息中的本地变量名来访问它们。例如,SpringFramework 就是这样实现 @ 路径变量的,所以可能存在一个可以调用的库方法。因为抽象接口方法没有方法体,所以在 Java7中不存在接口方法的调试信息,在 Java8中也不存在 AFAIK。

其他新特性 主要是新的 API、对 HotSpot 的改进和工具。一些新的 API 可以作为第三方库使用(例如 310-后端口流支持)。

总结,默认方法需要新的 JVM 特性,而其他语言特性不需要。如果您想要使用它们,您需要用 Java8编译代码,然后用 逆行将字节码转换为 Java5/6/7格式。至少需要更改字节码版本,并且 javac 不允许 -source 1.8 -target 1.7,因此需要一个回溯翻译器。

你可以做 -source 1.7 -target 1.7,然后它将编译。但是如果你有 java8特定的特性,比如 lambdas,它就不能编译