如何在Java 11中编写代码,但针对Java 8及以上版本?

2022-08-31 17:39:36

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

当我尝试这个:

javac -source 11 -target 1.8 App.java

我收到以下消息:

warning: source release 11 requires target release 11

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

$ 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)转换为低级(字节码)似乎很容易。在我看来,当高级语言发生变化时,低级语言应该保持不变。这就是为什么我认为这是可能的。

更新

伙计们,我不认为这个答案重复移动到OpenJDK-11,而是用Java 8编译,因为OP询问如何继续生成具有功能的代码,但目标是Java 11(这只是一个着名的向后兼容性)。我的问题是相反的:我想在Java 11中生成代码,但目标是Java 8。在提出问题之前,我在研究该主题时遇到了这个问题。我不认为它适用于我的情况。Java 8

另一个问题 Java 8 代码是否可以编译为在 Java 7 JVM 上运行,看起来确实与我的问题类似,但它是在 2013 年被问到的,字节码显然在 Java 7 和 Java 8 之间发生了变化。

自Java 8以来,我不认为字节码发生了太大变化,这就是我问这个问题的原因。


答案 1

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

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

它还引入了动态常量,尽管我不知道Java语言是否在任何地方利用了该功能。这主要用于将来的版本。

然后,从JDK 9开始,字符串串联使用引用java.lang.invoke.StringConcatFactory进行编译,这在Java 8中不存在。invokedynamic

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

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


答案 2

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

就而言,参数不能大于参数。javac-source-target

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

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

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

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


推荐