为什么缺少的注释在运行时不会导致ClassNotFoundException?


91

考虑以下代码:

A.java:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

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

C.java:

import java.util.*;

@A public class C {
        public static void main(String[] args){
                System.out.println(Arrays.toString(C.class.getAnnotations()));
        }
}

编译并按预期运行作品:

$ javac *.java
$ java -cp . C
[@A()]

但是请考虑一下:

$ rm A.class
$ java -cp . C
[]

我本来希望它抛出一个ClassNotFoundException,因为@A丢失了。但是,它会静默删除注释。

是在JLS的某个地方记录了此行为,还是Sun的JVM的怪癖?这样做的理由是什么?

看起来很方便javax.annotation.Nonnull@Retention(CLASS)反之亦然),但对于许多其他注释,它似乎可能导致运行时发生各种不良情况。

Answers:


90

在早期的JSR-175(注释)公共草案中,讨论了编译器和运行时是否应忽略未知注释,以在注释的使用和声明之间提供更宽松的联系。一个特定的示例是在EJB上使用应用程序服务器特定的注释来控制部署配置。如果应将同一bean部署在不同的应用程序服务器上,那么如果运行时仅忽略未知的注释而不是引发NoClassDefFoundError,那将很方便。

即使措辞有点含糊,我也认为您所看到的行为是在JLS 13.5.7中指定的:“ ...删除注释对Java编程语言中程序的二进制表示形式的正确链接没有影响。 。” 我将其解释为好像已删除注释(在运行时不可用),程序仍应链接并运行,这意味着在通过反射访问时,未知的注释将被简单地忽略。

Sun的JDK 5的第一个发行版未正确实现此功能,但已在1.5.0_06中修复。您可以在错误数据库中找到相关的错误6322301,但是它没有指向任何规范,只是声称“根据JSR-175规范负责人,getAnnotations必须忽略未知的注释”。


35

引用JLS:

9.6.1.2保留注释只能在源代码中出现,也可以以类或接口的二进制形式出现。二进制文件中存在的注释在运行时可能会或可能不会通过Java平台的反射库提供。

注释类型注解。保留用于选择以上可能性。如果注释a对应于类型T,并且T具有与metament.Retention对应的(元)注释m,则:

  • 如果m具有其元素值为注解.RetentionPolicy.SOURCE的元素,则Java编译器必须确保a在出现a的类或接口的二进制表示中不存在。
  • 如果m有一个元素的值是注解.RetentionPolicy.CLASS或注解.RetentionPolicy.RUNTIME,则Java编译器必须确保a在出现a的类或接口的二进制表示中表示,除非m注释了局部变量声明。本地变量声明的注释永远不会保留在二进制表示中。

如果T没有对应于注解.Retention的(元注解)m,那么Java编译器必须将T视为确实具有这样的元注解m和元素,其值是注解.RetentionPolicy.CLASS。

因此,RetentionPolicy.RUNTIME可确保将注释编译到二进制文件中,但二进制文件中存在的注释不必在运行时可用


9

如果您实际上有读取@A并对其执行某些操作的代码,则该代码对类A具有依赖性,并且它将引发ClassNotFoundException。

如果不是这样,即没有代码特别关心@A,那么@A并不重要。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.