如何在Java 11中生成代码,但目标是Java 8及更高版本?

问题描述 投票:8回答:4

我正在开发一个小型库,出于显而易见的原因,我想使用所有Java 11功能(我现在想的模块除外)生成代码,但我希望该库与Java 8及更高版本兼容。

当我尝试这个:

javac -source 11 -target 1.8 App.java

我收到以下消息:

warning: source release 11 requires target release 11

...当我查看字节代码时,我看到该类的版本是0x37(Java 11):

$ xxd App.class
00000000: cafe babe 0000 0037 ...

Java 8无法加载它:

Exception in thread "main" java.lang.UnsupportedClassVersionError: App has been
    compiled by a more recent version of the Java Runtime (class file version 55.0),
    this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

人们如何提供这种兼容性?我对所有构建工具持开放态度。

对我来说,将高级语言(Java)转换为低级(字节码)似乎很容易。在我看来,当高级语言发生变化时,低级语言应该保持不变。这就是为什么我认为这是可能的。

UPDATE

大家好,我不认为这个答案与Move to OpenJDK-11 but compile in Java 8重复,因为OP询问如何继续使用Java 8功能生成代码,但目标Java 11(这只是一个着名的向后兼容性)。我的问题是另一种方式:我想在Java 11中生成代码,但是以Java 8为目标。当我在提出问题之前研究这个主题时,我遇到了这个问题。我觉得它不适用于我的情况。

另一个问题Can Java 8 code be compiled to run on Java 7 JVM看起来与我的问题类似,但是在2013年被问到,字节码在Java 7和Java 8之间明显改变了。

我不认为字节码变化那么多,因为Java 8就是我问这个问题的原因。

java cross-compiling javac java-11
4个回答
4
投票

虽然使用复杂的工具在理论上可以将为JDK 11编译的类转换为JDK 8,但这并非易事。二进制级别有重大变化。

首先,JDK 11引入了nest类型,这样就无需在访问内部/外部类的private成员时生成合成访问器方法。当然,这种访问在旧版本中会失败。

它还引入了dynamic constants,但我不知道Java语言是否在任何地方利用了该功能。这主要用于未来版本。

然后,从JDK 9开始,使用引用invokedynamicjava.lang.invoke.StringConcatFactory编译字符串连接,这在Java 8中不存在。

一个可行的功能是接口中的private方法,在Java 9中作为语言特性引入,但已经在Java 8中的二进制级别上处理。

Java 8也无法处理模块定义,但我想,它们将被忽略。


6
投票

虽然我没有在javac的javadoc中看到任何明确的内容,但我认为你只能为-source和-target选项声明相同的版本。 Java 8中不支持Java 11功能,但事实恰恰相反,更高版本的Java版本可以运行在较低版本中编译的代码。因此,我认为编写编写到Java 11的代码不可能在Java 8中运行。


4
投票

我在这里可能是错的,但至少要知道,javac并不意味着以这种方式使用。

这里有点猜测:你可以试着看看--release 8 --target 8是否有效(没有给出--source 11参数)。

但我怀疑这会奏效。我认为javac不支持接受N源代码功能,并且已经向后编译到早期的目标版本。

当然,编译器可以了解将N源代码转换为(N-m)字节代码所需的转换。但这会使编译器变得更加复杂,每个版本都会增加。它还会增加测试工作的成本。我怀疑编译维护者是否愿意购买。这真的不是一个广泛的用例。

所以,我所知道的唯一“解决方案”:分支和双重维护。为了保持合理,我只保留一个Java 8版本,也许一个用于Java 11。


1
投票

不,您无法将Java 11源代码编译为Java 8二进制文件。

javac术语中,-source参数不能大于-target参数。

因此,如果要生成Java 8二进制文件,则应使用java 8(或更早版本)编写源代码。如果您不使用任何Java 11语言功能,那么您的源代码基本上已经在Java 8中,因此这不应该是一个太大的问题。

请注意,您仍然可以使用JDK 11将Java 8源代码编译为Java 8二进制文件。 JDK版本可以大于源和/或目标版本。

注意:javac文档没有说明-source参数必须小于或等于-target参数。但是,有很多非官方文档。例如,https://stackoverflow.com/a/9261298/691074

据我所知,也没有一个反例可以让这种情况真正起作用。

© www.soinside.com 2019 - 2024. All rights reserved.