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

Gar*_*son 3 java annotations runtime maven

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

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

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

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

Cry*_*jar 9

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

其实,这是不是可以通过编译新的源代码版本的Java源文件为老版本的JVM版本的目标.Oracles和OpenJDK 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 {
}
Run Code Online (Sandbox Code Playgroud)

如果编译,它会产生a Test.class和a TestAnno.class.执行时程序输出:

@TestAnno()
Run Code Online (Sandbox Code Playgroud)

因为这是应用的一个注释Test.现在,如果TestAnno.class在没有任何修改,删除Test.class(这是指TestAnnoLTestAnno;在字节码序列),并Test再次执行,它只是做任何输出.所以我的JVM确实忽略了缺少的注释,并且没有生成任何错误或异常(在Linux上使用OpenJDK版本1.8.0_131进行了测试).