简体   繁体   English

Java运行时保留批注在以前的Java版本中的兼容性

[英]Compatibility of a Java runtime retention annotation in previous Java versions

I want to use the @FunctionalInterface from Java 8 in my code, but I want to be able to use the generated class files with Java 6. I think then that I should the source version to 1.8 , and the target version to 1.6 . 我想在我的代码中使用Java 8中的@FunctionalInterface ,但我希望能够将生成的类文件与Java 6一起使用。我认为我应该将源版本设置为1.8 ,将目标版本设置为1.6

I would be using @FunctionalInterface just for documentation, but I note that it has @Retention(RetentionPolicy.RUNTIME) . 我将使用@FunctionalInterface仅用于文档,但我注意到它有@Retention(RetentionPolicy.RUNTIME) If no one ever uses that annotation, will it cause problems? 如果没有人使用该注释,它会导致问题吗?

If someone iterates over the annotations of my object at runtime, will it cause a missing class exception? 如果有人在运行时迭代我的对象的注释,它是否会导致缺少类异常? But if that is true, how is it that how Google Guava can declare the JSR 305 annotation dependency to have a Maven <scope> of provided, which means annotations such as javax.annotation.Nonnull are missing at runtime, too, in Guava, without causing problems? 但是,如果这是真的,那么Google Guava如何声明JSR 305注释依赖关系以提供Maven <scope> ,这意味着在运行时,在Guava中, javax.annotation.Nonnull等注释也会丢失。没有造成问题?

Let me ask it another way: if I use Google Guava in my project but don't include a JSR 305 dependency, do I really risk some error if I use reflection on the code? 让我用另一种方式问:如果我在项目中使用Google Guava但不包含JSR 305依赖项,如果我对代码使用反射,我是否真的会冒一些错误? If so, what error will occur? 如果是这样,会发生什么错误? If no error will occur, then analogously can I use the @FunctionalInterface annotation in source compiled with Java version 1.8 yet targeted to version 1.6 without any risk of runtime errors, even using reflection? 如果不会发生错误,那么类似地,我可以在使用Java版本1.8编译的源代码中使用@FunctionalInterface注释,但是针对版本1.6而没有任何运行时错误的风险,即使使用反射也是如此?

I think then that I should [set] the source version to 1.8 , and the target version to 1.6 . 我认为那时我应该[设置]源版本为1.8 ,目标版本为1.6

Actually, it is not possible to compile Java source files of newer source versions for older JVM target versions. 其实,这是不可能的编译更新的源代码版本的Java源文件为老版本的JVM版本的目标。 Oracles and OpenJDKs javac will reject a compilation attempt where the -source version is higher than the -target version. Oracles和OpenJDKs javac将拒绝-source版本高于-target版本的编译尝试。 (However, I couldn't find a specification denying it, even the manual doesn't mention that). (但是,我找不到拒绝它的规范,即使手册没有提到它)。 The sole idea of javac s cross-compiling feature is that you can compile your old eg 1.6 Java files still for the old 1.6 JVM even when you are using a newer JDK for compilation. javac的交叉编译功能的唯一想法是,即使您使用较新的JDK进行编译,您仍可以为旧的1.6 JVM编译旧的1.6 Java文件。

The issue you are describing is the sort of reason for this. 您描述的问题是这样的原因。 Since Java is using a sort of lazy dependency loading, the compiler can't guarantee that there will be an appropriated class at runtime for all the dependencies. 由于Java使用了一种延迟依赖性加载,因此编译器无法保证在运行时对所有依赖项都有适当的类。 This also applies to the standard library. 这也适用于标准库。

However, there are (unofficial) tools to compile the newer source idioms or byte code to older byte code versions. 但是,有(非官方)工具可以将较新的源惯用法或字节代码编译为较旧的字节代码版本。 But that doesn't go for the standard library. 但这不适用于标准库。 If you wanna use newer classes, you have to provide them on your own. 如果你想使用更新的课程,你必须自己提供。 For this purpose, there exist some back ports for specific parts of the standard library. 为此,标准库的特定部分存在一些后端口。

Specifically about your annotation question: 特别是关于您的注释问题:

I was not able to find any reliable specification to what should/might happen if the JVM encounters an annotated construct for which it could not retrieve the class file (I searched the Java virtual machine specification SE 8 ). 如果JVM遇到无法检索类文件的带注释的构造(我搜索了Java虚拟机规范SE 8 ),我无法找到任何应​​该/可能发生的可靠规范。 However, I found a somewhat related reference in the Java language specification SE 8 : 但是,我在Java语言规范SE 8中找到了一些相关的参考:

An annotation is a marker which associates information with a program construct, but has no effect at run time. 注释是将信息与程序构造相关联的标记,但在运行时没有影响。

From JLS 9.7 来自JLS 9.7

This statement rather indicates that an annotation (present or not) should not have an influence on the execution of a JVM. 此语句反而表明注释(是否存在)不应对JVM的执行产生影响。 Therefore, a exception (such as NoClassDefFoundError ) because of a missing annotation were rather against this. 因此,由于缺少注释而导致的异常(例如NoClassDefFoundError )更倾向于此。

Finally, though the answers of this question , I found even more specific statements: 最后,虽然这个问题的答案,我发现了更具体的陈述:

An annotation that is present in the binary form may or may not be available at run time via the reflection libraries of the Java SE platform. 二进制形式中存在的注释可能在运行时通过Java SE平台的反射库提供,也可能不提供。

From JLS 9.6.4.2 来自JLS 9.6.4.2

And

Adding or removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language. 添加或删除注释对Java编程语言中程序的二进制表示的正确链接没有影响。

From JLS 13.5.7 来自JLS 13.5.7

This quite clearly states that missing annotations will not cause an error , but instead will be just ignored if examined by reflection. 这非常明确地指出,缺少注释不会导致错误 ,但是如果通过反射检查则会被忽略。 And if you deliver a class annotated with a Java 1.8 standard library annotation, and it will be (somehow) executed on eg Java 1.6 JVM where that annotation is just not present, then this specifications denies that any error is generated. 如果你提供一个Java 1.8标准库注解注释的类,这将是(在某种程度上)上例如,Java 1.6 JVM执行如该注释是不存在,那么这个规格否认产生任何错误。

This is also supported by the following test which I wrote: (notice the usage of reflection) 我写的以下测试也支持这一点:( 注意反射的用法)

@TestAnno
public class Test {
  public static void main(String[] args) {
    Annotation[] annos = Test.class.getAnnotations();
    for (Annotation a : annos) {
      System.out.println(a);
    }
  }
}

@Retention(RetentionPolicy.RUNTIME)
@interface TestAnno {
}

If compiled, it yields a Test.class and a TestAnno.class . 如果编译,它会产生Test.classTestAnno.class When executed the program outputs: 执行时程序输出:

@TestAnno()

Because that is the one annotation applied to Test . 因为这是应用于Test的一个注释。 Now, if the TestAnno.class is removed without any modifications to Test.class (which refers to TestAnno with LTestAnno; sequence in the byte code) and Test is executed again, it just does not output anything . 现在,如果TestAnno.class而不以任何修改删除Test.class (指TestAnnoLTestAnno;在字节码序列)和Test再次被执行,它只是不输出任何东西 So my JVM is indeed ignoring the missing annotation and does not generate any error or exception (Tested with a OpenJDK version 1.8.0_131 on Linux). 所以我的JVM确实忽略了缺少的注释,并且没有生成任何错误或异常(在Linux上使用OpenJDK版本1.8.0_131进行了测试)。

As with any class loading situation, if the class isn't needed (or rather, doesn't need to be loaded), it doesn't matter if the class doesn't exist at runtime. 与任何类加载情况一样,如果不需要类(或者更确切地说,不需要加载),那么在运行时该类是否不存在并不重要。 Runtime annotations normally have the same problem, since if they're retained at runtime, it usually means that there's logic based on them, meaning their classes are loaded too. 运行时注释通常具有相同的问题,因为如果它们在运行时保留,通常意味着存在基于它们的逻辑,这意味着它们的类也被加载。

But @FunctionalInterface doesn't have runtime logic, so... Why does @FunctionalInterface have a RUNTIME retention? @FunctionalInterface没有运行时逻辑,所以... 为什么@FunctionalInterface有RUNTIME保留? Apparently not for any particularly compelling reason, just a side effect of it also being @Documented annotation. 显然不是出于任何特别令人信服的理由,只是它的副作用也是@Documented注释。

So if you want to make sure there are no potential problems if someone (or some tool more likely (I don't mean a "tool", like a co-worker)) decides to enumerate the annotations in your classes, I guess you'd need to remove the annotations at pre-processing. 因此,如果你想确保没有潜在的问题,如果某人(或某些工具更可能(我不是指“工具”,如同事))决定列举你班级的注释,我想你需要在预处理时删除注释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM