繁体   English   中英

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

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

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

我将使用@FunctionalInterface仅用于文档,但我注意到它有@Retention(RetentionPolicy.RUNTIME) 如果没有人使用该注释,它会导致问题吗?

如果有人在运行时迭代我的对象的注释,它是否会导致缺少类异常? 但是,如果这是真的,那么Google Guava如何声明JSR 305注释依赖关系以提供Maven <scope> ,这意味着在运行时,在Guava中, javax.annotation.Nonnull等注释也会丢失。没有造成问题?

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

我认为那时我应该[设置]源版本为1.8 ,目标版本为1.6

其实,这是不可能的编译更新的源代码版本的Java源文件为老版本的JVM版本的目标。 Oracles和OpenJDKs javac将拒绝-source版本高于-target版本的编译尝试。 (但是,我找不到拒绝它的规范,即使手册没有提到它)。 javac的交叉编译功能的唯一想法是,即使您使用较新的JDK进行编译,您仍可以为旧的1.6 JVM编译旧的1.6 Java文件。

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

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

特别是关于您的注释问题:

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

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

来自JLS 9.7

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

最后,虽然这个问题的答案,我发现了更具体的陈述:

二进制形式中存在的注释可能在运行时通过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 (指TestAnnoLTestAnno;在字节码序列)和Test再次被执行,它只是不输出任何东西 所以我的JVM确实忽略了缺少的注释,并且没有生成任何错误或异常(在Linux上使用OpenJDK版本1.8.0_131进行了测试)。

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

@FunctionalInterface没有运行时逻辑,所以... 为什么@FunctionalInterface有RUNTIME保留? 显然不是出于任何特别令人信服的理由,只是它的副作用也是@Documented注释。

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

暂无
暂无

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

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