请考虑以下代码:
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)
)但是对于许多其他注释来说,似乎它可能会导致在运行时发生各种不好的事情。
答案 0 :(得分:83)
在早期的JSR-175(注释)公共草案中,讨论了编译器和运行时是否应该忽略未知注释,以便在注释的使用和声明之间提供更松散的耦合。一个具体示例是在EJB上使用应用程序服务器特定的注释来控制部署配置。如果同一个bean应该部署在不同的应用程序服务器上,那么如果运行时只是忽略了未知的注释而不是引发NoClassDefFoundError,那将会很方便。
即使措辞有点含糊,我也认为你所看到的行为是在JLS 13.5.7中指定的:“...删除注释对正确连接程序的二进制表示没有影响。 Java编程语言。“我将此解释为好像删除了注释(在运行时不可用),程序仍应链接并运行,这意味着在通过反射访问时,将忽略未知注释。
Sun的JDK 5的第一个版本没有正确实现,但它已在1.5.0_06中修复。您可以在bug数据库相关的错误6322301,但它并不指向任何规格,除了声称“根据JSR-175规范领导,未知注释必须由getAnnotations忽略。”
答案 1 :(得分:32)
引用JLS:
9.6.1.2保留注释可能仅出现在源代码中,或 它们可能以二进制形式存在 类或接口的。 注释 存在于二进制文件中的可能或 可能无法在运行时通过 Java的反射库 平台。强>
注释类型 annotation.Retention用于选择 在上述可能性中。如果 注释a对应于类型T, 和T有一个(meta-)注释 对应注释。注意, 然后:
- 如果m有一个元素,其值为annotation.RetentionPolicy.SOURCE, 那么Java编译器必须确保这一点 a不存在于二进制文件中 代表班级或 a出现的界面。
- 如果m有一个元素,其值为annotation.RetentionPolicy.CLASS,或 annotation.RetentionPolicy.RUNTIME a Java编译器必须确保a 以二进制表示 代表班级或 出现a的界面,除非m 注释局部变量 宣言。关于本地的注释 变量声明永远不会保留 在二进制表示中。
如果T没有(meta-)注释 m对应于 annotation.Retention,然后是Java 编译器必须像对待T一样对待T 有一个这样的元注释 元素的值是 annotation.RetentionPolicy.CLASS。
因此,RetentionPolicy.RUNTIME确保将注释编译为二进制文件,但二进制文件中存在的注释不必在运行时可用
答案 2 :(得分:7)
如果您实际上有代码读取@A并对其执行某些操作,则代码依赖于A类,并且它将抛出ClassNotFoundException。
如果没有,即没有代码特别关注@A,那么可以说@A并不重要。