在更高版本中使用Java 8目标如何覆盖javac 11链接方法? [英] How should javac 11 link methods overridden in later versions with a Java 8 target?

查看:88
本文介绍了在更高版本中使用Java 8目标如何覆盖javac 11链接方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在使用 Java 11 javac ,但是我使用的是--source--target选项设置为1.8,因此我的源代码将被视为Java 8,输出的.class文件将与Java 8兼容.我的目标是生成可以在Java 8 JVM上运行的.class文件.

Let's say I'm using Java 11 javac, but I'm using the --source and --target options set to 1.8 so that my source code will be considered Java 8 and the output .class files will be compatible with Java 8. My goal is to produce .class files that can run on a Java 8 JVM.

假设我正在编译以下Java 8代码:

And let's say I have the following Java 8 code I'm compiling:

import java.nio.ByteBuffer;

…

ByteBuffer byteBuffer = …; //init somehow
byteBuffer.flip(); //what ends up in the `.class` file?

问题是:Java 11 javac应该在.class文件中放置什么以链接byteBuffer.flip()方法调用?在回答之前,请考虑以下问题:

The question is: what should Java 11 javac put in the .class file to link the byteBuffer.flip() method call? Before you answer, consider this:

  • Neither Java 8 ByteBuffer nor Java 11 ByteBuffer declare the flip() method at the ByteBuffer level.
  • ByteBuffer is a direct subclass of Buffer. There is both a Java 8 Buffer.flip() and a Java 11 Buffer.flip() declared in the Buffer API for both versions.
  • In the Java 8 source code, there is no ByteBuffer.flip() method.
  • But in the Java 11 source code, ByteBuffer overrides the Buffer.flip() method like the following. The purpose apparently was to use covariance so that in Java 11 ByteBuffer.flip() would conveniently return a ByteBuffer instead of a Buffer.
@Override
public ByteBuffer flip() {
    super.flip();
    return this;
}

因此要重申这个问题:将Java 11 javac(--source--target选项设置为1.8)是否应生成链接到Buffer.flip()ByteBuffer.flip().class文件?如果是前者,那么它怎么知道不包括ByteBuffer.flip()呢,因为(Java 8)代码清楚地引用了ByteBuffer.flip()并且(Java 11)编译器看到运行时中有一个ByteBuffer.flip()方法?但是如果是后者,那么我怎么能知道即使使用--source--target选项,使用Java 11编译的100%正确的Java 8兼容源代码也可以在Java 8 JRE上运行.表示Java 8? (请注意,OpenJDK 11.0.5似乎选择了后者.但这是正确的吗?)

So to restate the question: Should Java 11 javac, with the --source and --target options set to 1.8, generate a .class file that links to Buffer.flip() or to ByteBuffer.flip()? If it is the former, then how does it know not to include ByteBuffer.flip() instead, as the (Java 8) code clearly references ByteBuffer.flip() and the (Java 11) compiler sees that there is a ByteBuffer.flip() method in the runtime? But if it is the latter, then how can I ever know that my 100% correct Java 8 compatible source code, when compiled using Java 11, will run on a Java 8 JRE even if I use the --source and --target options to indicate Java 8? (Note that OpenJDK 11.0.5 seems to choose the latter option. But which is correct?)

(请注意,我经常使用"link"一词;我目前并不精通所生成的字节码.我所知道的是,类文件已经以某种方式引用了Buffer.flip()ByteBuffer.flip();如果在运行时找不到此方法,则JVM将抛出异常,例如:java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;.)

(Note that I'm using the word "link" loosely; I'm not currently well-versed in what bytecode is generated. All I know is that the class file has come reference somehow to Buffer.flip() or ByteBuffer.flip(); and if this method can't be found at runtime, the JVM will throw an exception such as: java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;.)

作为一个额外的问题,我想知道是否使用为Java 8设置的--release选项是否会改变答案.但是请注意,我不能使用--release选项(等同于Maven <release>编译器插件选项),因为我希望我的Maven项目能够同时使用Java 8和Java 11进行构建.

As a bonus question, I wonder whether using the --release option set for Java 8 would change the answer. But note that I can't use the --release option (equivalent to the Maven <release> compiler plugin option) because I want my Maven project to be buildable with both Java 8 and Java 11.

推荐答案

如果我们采用以下代码,并使用Java 8和Java 11进行编译,则将获得以下字节码,如运行javap -c MyClass.class时所见.

If we take the following code and compile with Java 8 and with Java 11, we get the following bytecode, as seen when running javap -c MyClass.class.

Java源代码

ByteBuffer byteBuffer = ByteBuffer.allocate(64);
byteBuffer.flip();

Java 8字节码

 0: bipush        64
 2: invokestatic  #19   // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
 5: astore_1
 6: aload_1
 7: invokevirtual #25   // Method java/nio/ByteBuffer.flip:()Ljava/nio/Buffer;
10: pop

Java 11字节码

 0: bipush        64
 2: invokestatic  #19   // Method java/nio/ByteBuffer.allocate:(I)Ljava/nio/ByteBuffer;
 5: astore_1
 6: aload_1
 7: invokevirtual #25   // Method java/nio/ByteBuffer.flip:()Ljava/nio/ByteBuffer;
10: pop

如您所见,它们都链接"到了到ByteBufferflip()方法,即使Java 8中未声明该方法也是如此.

As you can see, both of them "link" to the flip() method of ByteBuffer, even though the method isn't declared there for Java 8.

但是,在字节码级别,方法签名包括返回类型.这意味着JVM支持的语言可以重载仅在返回类型上有所不同的方法,即使Java不支持.

However, at the bytecode level, method signatures include the return type. This means that the JVM supports languages where you can overload methods that differ only in return type, even though Java doesn't support that.

该方法的Java 11版本具有不同的返回类型,可以在链接"窗口中看到.方法,在()之后,其中Java 8将返回类型显示为Ljava/nio/Buffer;,而Java 11将返回类型显示为Ljava/nio/ByteBuffer;.

The Java 11 version of the method has a different return type, which can be seen in the "linked" method, following the (), where Java 8 shows return type as Ljava/nio/Buffer; and Java 11 shows return type as Ljava/nio/ByteBuffer;.

当您使用针对Java 11 Runtime Library编译的代码并尝试在Java 8上运行它时,会得到Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;

When you take the code that was compiled against the Java 11 Runtime Library, and you try running it on Java 8, you get Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;

这就是为什么您应该始终始终指定 bootstrap类路径,以指向与目标Java版本匹配的Java运行时库的原因.当使用带有选项-source 8 -target 8的Java 11的javac进行编译时,它实际上会向您发出警告:

This is why you should always specify the bootstrap class path to point to a Java Runtime Library matching the target Java version. When you compile using Java 11's javac with options -source 8 -target 8, it will actually warn you about that:

warning: [options] bootstrap class path not set in conjunction with -source 8
1 warning

这就是为什么他们实现了更新的--release <release>选项来替换-source-target的原因.如果使用--release 8进行编译,则生成的.class文件将在Java 8上正常运行.

This is why they implemented the newer --release <release> option to replace -source and -target. If you compile with --release 8, the generated .class file will run without error on Java 8.

更新

您不需要安装Java 8即可使用选项--release 8. Java 11安装知道Java 8 Runtime Library的方法是什么.

You don't need Java 8 installed to use option --release 8. The Java 11 installation knows what the methods of the Java 8 Runtime Library were.

由于 JEP 247:针对较旧的平台版本进行了编译,因此在Java 9中实现了--release选项. ,它表示:

The --release option was implemented in Java 9 as a result of JEP 247: Compile for Older Platform Versions, which says:

对于JDK N和--release M,M< N,需要平台M的已记录API的签名数据.此数据存储在$JDK_ROOT/lib/ct.sym文件中,该文件与JDK 8中相同名称的文件相似但不相同.ct.sym文件是一个ZIP文件,其中包含与类文件相对应的精简类文件.从目标平台版本开始.

For JDK N and --release M, M < N, signature data of the documented APIs of release M of the platform is needed. This data is stored in the $JDK_ROOT/lib/ct.sym file, which is similar, but not the same, as the file of the same name in JDK 8. The ct.sym file is a ZIP file containing stripped-down class files corresponding to class files from the target platform versions.

这篇关于在更高版本中使用Java 8目标如何覆盖javac 11链接方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆