请考虑以下代码:
A.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
@interface A{}
Run Code Online (Sandbox Code Playgroud)
C.java:
import java.util.*;
@A public class C {
public static void main(String[] args){
System.out.println(Arrays.toString(C.class.getAnnotations()));
}
}
Run Code Online (Sandbox Code Playgroud)
编译和运行按预期工作:
$ javac *.java
$ java -cp . C
[@A()]
Run Code Online (Sandbox Code Playgroud)
但是考虑一下:
$ rm A.class
$ java -cp . C
[]
Run Code Online (Sandbox Code Playgroud)
我会期望它抛出一个ClassNotFoundException
,因为@A
缺少.但相反,它会默默地删除注释.
这种行为是在JLS的某个地方记录的,还是Sun的JVM的怪癖?它的基本原理是什么?
这样的事情似乎很方便javax.annotation.Nonnull
(看起来应该是这样@Retention(CLASS)
的)但是对于许多其他注释来说,似乎它可能会导致在运行时发生各种不好的事情.
jar*_*bjo 85
在早期的JSR-175(注释)公共草案中,讨论了编译器和运行时是否应该忽略未知注释,以便在注释的使用和声明之间提供更松散的耦合.一个具体示例是在EJB上使用应用程序服务器特定的注释来控制部署配置.如果同一个bean应该部署在不同的应用程序服务器上,那么如果运行时只是忽略了未知的注释而不是引发NoClassDefFoundError就会很方便.
即使措辞有点模糊,我也认为你所看到的行为是在JLS 13.5.7中指定的:"...删除注释对Java编程语言中程序的二进制表示的正确链接没有影响".我将此解释为好像删除了注释(在运行时不可用),程序仍应链接并运行,这意味着在通过反射访问时,将忽略未知注释.
Sun的JDK 5的第一个版本没有正确实现,但它已在1.5.0_06中修复.您可以在错误数据库中找到相关的错误6322301,但它没有指出任何规范,只是声称"根据JSR-175规范的引导,getAnnotations必须忽略未知的注释".
Gui*_*ume 32
引用JLS:
9.6.1.2保留注释可能仅存在于源代码中,或者它们可能以类或接口的二进制形式存在.二进制文件中存在的注释可能在运行时通过Java平台的反射库提供或不可用.
注释类型注释.注释用于在上述可能性中进行选择.如果注释a对应于类型T,并且T具有对应于注释的(元)注释m,则:
- 如果m有一个元素,其值为annotationPolicy.SOURCE,则Java编译器必须确保在出现a的类或接口的二进制表示中不存在a.
- 如果m有一个元素,其值为annotation.RetentionPolicy.CLASS或annotation.RetentionPolicy.RUNTIME,Java编译器必须确保a在出现a的类或接口的二进制表示中表示,除非m注释本地变量声明.局部变量声明的注释永远不会保留在二进制表示中.
如果T没有对应于annotation.Retention的(meta-)注释m,则Java编译器必须将T视为具有这样的元注释m,其元素的值为annotation.RetentionPolicy.CLASS.
因此RetentionPolicy.RUNTIME确保将注释编译到二进制文件中,但二进制文件中存在的注释不必在运行时可用
如果您实际上有代码读取@A并对其执行某些操作,则代码依赖于类A,并且它将抛出ClassNotFoundException.
如果没有,即没有代码特别关注@A,那么可以说@A并不重要.