167

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

语言的这些变化是否伴随着编译后的字节码的重大变化,会阻止它在不使用某些逆向翻译器的情况下在 Java 7 虚拟机上运行?

4

5 回答 5

149

不,在源代码中使用 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
于 2014-03-18T22:30:24.427 回答
60

默认方法需要对字节码和 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 调用动态时 Java 8 在运行时所做的相同。

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

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

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

其他新功能主要是新的 API、对 HotSpot 和工具的改进。一些新的 API 可作为 3rd 方库使用(例如ThreeTen-Backportstreamsupport)。

Summa summarum,默认方法需要新的 JVM 特性,但其他语言特性不需要。如果要使用它们,则需要在 Java 8 中编译代码,然后使用Retrolambda将字节码转换为 Java 5/6/7 格式。至少需要更改字节码版本,并且 javac 不允许-source 1.8 -target 1.7,因此需要逆向翻译器。

于 2014-05-20T10:18:24.353 回答
33

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

也许以下参考资料可以帮助加深对如何检测与 lambda 相关的更改的理解。

这些详细解释了事物是如何在引擎盖下进行检测的。也许您可以在那里找到问题的答案。

于 2013-04-29T15:03:31.787 回答
11

如果您愿意使用“retrotranslator”,请尝试 Esko Luontola 出色的 Retrolambda:https ://github.com/orfjackal/retrolambda

于 2014-05-17T09:39:19.413 回答
-6

你可以这样做,-source 1.7 -target 1.7然后它会编译。但是如果你有 java 8 特定的特性,比如 lambda,它就不会编译

于 2014-12-20T15:33:38.877 回答