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


163

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

语言的这些变化是否伴随着已编译字节码的重大变化,从而阻止了它在Java 7虚拟机上运行而无需使用某些逆向转换程序?


Answers:


146

不可以,在源代码中使用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

4
不,我认为不会。Java在台式机市场上占有很小的份额,但是却保持着很小的份额。但这确实妨碍了新版本和功能的采用。在相当长的一段时间内,我将无法在编写的代码中使用Java 8功能,因为我希望避免人们不得不升级其本地Java安装。
JesperE 2014年

为什么?“是”表示可以将Java 8编译为在Java 7 VM上运行,根据Java 8编译器,这是不正确的。
JesperE 2014年

5
现在,我看到:您的“否”回答问题的标题,而不是问题的正文。
阿卜杜勒2014年

58

默认方法需要对字节码和JVM进行此类更改,而这些更改在Java 7上是无法实现的。Java 7及更低版本的字节码验证程序将拒绝具有方法主体的接口(静态初始化方法除外)。尝试在调用方使用静态方法模拟默认方法不会产生相同的结果,因为默认方法可以在子类中覆盖。Retrolambda对回传默认方法的支持有限,但是永远无法完全回传,因为它确实需要新的JVM功能。

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

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

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

方法参数名称存在于Java 7的字节码中,因此也是兼容的。您可以通过读取方法的字节码并查看方法的调试信息中的局部变量名称来访问它们。例如,Spring Framework正是通过实现@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因此需要Retrotranslator。


3
实际上,类型注释可以在运行时可见。stackoverflow.com/questions/22374612/…–

33

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

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

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


7
没有新的字节码,而是新的结构。验证者将呕吐。
Jonathan S. Fisher

12
一个很好的例子是接口。它们现在可以包含方法。Java7验证程序不具备处理此问题的能力。所有旧字节码均已使用,但以新方式使用。
乔纳森·费舍尔

1
我想知道具有这么多语言功能的scala编译器如何实现甚至jdk5的目标jvm版本。
马里诺斯(Marinos)

1
@MarinosAn你到底是什么意思?MI与包含具体的方法,例如特点class C extends A with B,与普通接口来实现的A,并B和同伴类A$classB$class。类C将方法简单地转发到静态伴随类。自我类型根本不被强制执行,lambda在编译时被转换为抽象内部类,new D with A with B表达式也是如此。模式匹配是一堆if-else结构。非本地退货?lambda的try-catch机制。还剩下什么吗 (有趣的是,我的标尺说1.6是默认值)
Adowrath '17

1
当然,自类型等被编码在特殊的类属性和注释中,以便在使用已编译的类时,scalac可以使用和强制执行规则。
Adowrath


-5

您可以这样做-source 1.7 -target 1.7,它将进行编译。但是,如果您具有lambdas之类的Java 8特定功能,它将无法编译


这个问题明确地假定了新语言功能的使用,因此-source 1.7不会解决。
toolforger
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.