Java 8 代码可以编译为在 Java 7 JVM 上运行吗?

2022-08-31 07:04:14

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

语言中的这些更改是否伴随着编译字节码中的如此重大的更改,从而阻止它在不使用某些反转录翻译器的情况下在Java 7虚拟机上运行?


答案 1

否,在源代码中使用 1.8 功能需要以 1.8 VM 为目标。我刚刚尝试了新的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

答案 2

默认方法需要对字节码和 JVM 进行这样的更改,这在 Java 7 上是不可能做到的。Java 7 及更低版本的字节码验证器将拒绝具有方法主体的接口(静态初始值设定项方法除外)。尝试在调用方使用静态方法模拟默认方法不会产生相同的结果,因为可以在子类中重写默认方法。Retrolambda对向后移植默认方法的支持有限,但它永远无法完全向后移植,因为它确实需要新的JVM功能。

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

重复注释只是语法上的糖。它们与以前的版本兼容字节码。在Java 7中,你只需要自己实现帮助器方法(例如getAnnotationsByType),这些方法隐藏了包含重复注释的容器注释的实现细节。

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

方法参数名称存在于 Java 7 的字节码中,因此这也是兼容的。您可以通过读取方法的字节码并在方法的调试信息中查看局部变量名称来访问它们。例如,Spring框架正是为了实现@PathVariable,所以可能有一个库方法可以调用。由于抽象接口方法没有方法体,因此在 Java 7 中不存在接口方法的调试信息,而在 Java 8 上也不存在 AFAIK。

其他新功能主要是新的API,对HotSpot和工具的改进。一些新的API作为第三方库提供(例如ThreeTen-Backportstreamsupport)。

总而言之,默认方法需要新的JVM功能,但其他语言功能不需要。如果要使用它们,则需要编译Java 8中的代码,然后将带有Retrolambda的字节码转换为Java 5/6/7格式。至少需要更改字节码版本,并且javac不允许,因此需要反转录转换器。-source 1.8 -target 1.7


推荐