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

时间:2017-08-09 17:18:30

标签: java maven annotations runtime

我想在我的代码中使用Java 8中的@FunctionalInterface,但我希望能够将生成的类文件与Java 6一起使用。我认为我应该将源版本改为{{1} },目标版本为1.8

我会将1.6仅用于文档,但我注意到它有@FunctionalInterface。如果没有人使用该注释,它会导致问题吗?

如果有人在运行时迭代我的对象的注释,它是否会导致缺少类异常?但如果确实如此,Google Guava如何declare the JSR 305 annotation dependency提供Maven @Retention(RetentionPolicy.RUNTIME),这意味着在运行时也会遗漏诸如<scope>之类的注释。 ,没有造成问题?

让我用另一种方式问:如果我在项目中使用Google Guava但不包含JSR 305依赖项,如果我对代码使用反射,我是否真的会冒一些错误?如果是这样,会发生什么错误?如果不会发生错误,那么类似地,我可以在使用Java版本javax.annotation.Nonnull编译的源中使用@FunctionalInterface注释,但是针对版本1.8而没有任何运行时错误的风险,即使使用反射? / p>

2 个答案:

答案 0 :(得分:8)

  

我认为我应该将源版本设置为1.8,将目标版本设置为1.6

实际上,为较旧的JVM目标版本编译较新源版本的Java源文件 是不可能的。 Oracles和OpenJDK javac将拒绝-source版本高于-target版本的编译尝试。 (但是,我无法找到否定它的规范,即使manual也没有提到)。 javac交叉编译功能的唯一想法是你可以编译你的旧版本。 1.6即使您使用较新的JDK进行编译,Java文件仍旧用于旧的1.6 JVM。

您所描述的问题就是这种情况的原因。由于Java使用了一种延迟依赖性加载,因此编译器无法保证在运行时将为所有依赖项提供适当的类。这也适用于标准库。

但是,有(非官方的)工具可以将较新的源惯用法或字节代码编译为较旧的字节码版本。但这并不适用于标准库。如果你想使用更新的课程,你必须自己提供。为此,标准库的特定部分存在一些后端口。

特别关注您的注释问题:

如果JVM遇到无法检索类文件的注释构造(我搜索了Java virtual machine specification SE 8),我无法找到任何应​​该/可能发生的可靠规范。但是,我在Java language specification SE 8中找到了一些相关的参考:

  

注释是将信息与程序结构相关联的标记,但在运行时没有效果。

     

来自JLS 9.7

此语句反而表明注释(是否存在)不应对JVM的执行产生影响。因此,由于缺少注释而导致的异常(例如NoClassDefFoundError)更倾向于此。

最后,虽然this question的答案,我发现了更具体的陈述:

  

二进制形式中存在的注释可能在运行时通过Java SE平台的反射库提供,也可能不提供。

     

来自JLS 9.6.4.2

  

添加或删除注释对Java编程语言中程序的二进制表示的正确链接没有影响。

     

来自JLS 13.5.7

这非常明确地指出,缺失注释不会导致错误,但如果通过反射检查则会被忽略。 如果您使用Java 1.8标准库注释提供带有注释的类,并且它将以某种方式执行,例如, Java 1.6 JVM,其中没有该注释,此规范否认会生成任何错误。

我写的以下测试也支持这一点:(注意反射的用法)

@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 {
}

如果已编译,则会生成Test.classTestAnno.class。执行时程序输出:

@TestAnno()

因为这是应用于Test的一个注释。现在,如果删除TestAnno.class而未对Test.class进行任何修改(在字节代码中引用TestAnno并带有LTestAnno;序列),则再次执行Test ,它只是不输出任何东西。所以我的JVM确实忽略缺少的注释,并且生成任何错误或异常(在Linux上使用OpenJDK版本 1.8.0_131 测试)

答案 1 :(得分:0)

与任何类加载情况一样,如果不需要类(或者更确切地说,不需要加载),那么在运行时该类是否不存在并不重要。运行时注释通常具有相同的问题,因为如果它们在运行时保留,通常意味着存在基于它们的逻辑,这意味着它们的类也被加载。

但是@FunctionalInterface没有运行时逻辑,所以...... Why does @FunctionalInterface have a RUNTIME retention?显然不是出于任何特别令人信服的理由,只是它的副作用也是@Documented注释。

因此,如果你想确保没有潜在的问题,如果某人(或某些工具更可能(我不是指“工具”,如同事))决定枚举你班级中的注释,我想你需要在预处理时删除注释。