Annotation
Annotation是一个接口,程序可以通过反射来获取指定程序元素的Annotation对象,然后通过Annotation对象来取得注释里的元数据。
Annotation必须使用工具来处理,工具负责提取Annotation里包含的元数据,工具还会根据这些元数据增加额外的功能。使用Annotation时要在其前面增加@符号,并把该Annotation当成一个修饰符使用。
限定重写父类方法:@Override
@Override就是用来指定方法覆载的,它可以强制一个子类必须要覆盖父类的方法。只能作用于方法。
标示已过时:@Deprecated
@Deprecated用来表示某个程序元素已过时,当其他程序使用已过时的类、方法时,编译器便会给出警告。
抑制编译器警告:@SuppressWarnings
@SuppressWarnings指示被Annotation标识的程序元素取消显示指定的编译器警告。@SuppressWarnings会一直作用于该程序元素的所有子元素。
自定义Annotation
定义新的Annotation类型使用interface关键字,它用于定义一个新的Annotation类型。定义一个新的Annotation类型与定义一个接口非常像,如:
//定义一个简单的Annotation类型
public @interface Test{}
定义了该Annotation之后,就可以在程序任何地方来使用该Annotation,使用Annotation时的语法类似于public、final等修饰符。通常将其放在所有修饰符之前。
Annotation还可以带成员变量:
public @interface MyTag{
//定义了两个成员变量的Annotation
//Annotation中的成员变量以方法的形式来定义
String name();
int age();
}
一旦在Annotation里定义了成员变量之后,使用该Annotation时应该为该Annotation的成员变量指定值:
public class Test{
//使用带成员变量的Annotation时,需要为成员变量赋值
@Mytag(name="xx",age=6)
public void info(){}
}
在定义Annotation的成员变量时为其指定初始值,指定成员变量的初始值可使用default关键字。
public @interface MyTag{
//定义了两个成员变量的Annotation
//Annotation中的成员变量以方法的形式来定义
String name() default "yeeku";
int age() default 32;
}
Annotation分为两类:
- 标记Annotation:一个没有成员定义的Annotation类型被称为标记。这种Annotation仅使用自身的存在与否来为我们提供信息。
- 元数据Annotation:那些包含成员变量的Annotation,因为它们可接受更多元数据。
提取Annotation的信息
Java使用Annotation接口来代表程序元素前面的注释,该接口是所有Annotation类型的父接口。除此之外,Java在java.lang.reflect包下新增了AnnotatedElement接口,该接口代表程序中可以接受注释的程序元素,该接口主要有如下几个实现类:
- Class:类定义
- Constructor:构造器定义
- Field:类的成员变量定义
- Method:类的方法定义
- Package:类的包定义
java.lang.reflect包下主要包含一些实现反射功能工具类,实际上,java.lang.reflect包所提供的反射API扩充了读取运行时Annotation的能力。当一个Annotation类型被定义为运行时Annotation后,该注释才是运行时可见,当class文件被装载时被保存在class文件中的Annotation才会被虚拟机读取。
AnnotatedElement接口是所有程序元素(Class、Method和Constructor)的父接口,所以程序通过反射获取某个类的AnnotatedElement对象之后,程序就可以调用方法访问Annotation信息:
- getAnnotation(Class annotationClass):返回该程序元素上存在的、指定类型的注释,如果该类型的注释不存在,则返回null。
- Annotation[] getAnnotations():返回该程序元素上存在的所有注释。
- boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该程序元素上是否包含指定类型的注释,存在则返回true,否则返回false。
JDK的元Annotation
java.lang.annotation包下提供了四个Meta Annotation,这四个Annotation都是用于修饰其他Annotation定义。
使用@Retention
@Retention只能用于修饰一个Annotation定义,用于指定该Annotation可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。
value成员变量的值只能是下列三个:
- RetentionPolicy.Class:编译器将把注释记录在class文件中。当运行Java程序时,JVM不再保留注释。这是默认值。
- RetentionPolicy.RUNTIME:编译器将把注释记录在class文件中。当运行Java程序时,JVM也会保留注释,程序可以通过反射获取该注释。
- RetentionPolicy.SOURCE:编译器直接丢弃这种策略的注释。
使用@Documented
@Documented用于指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。
使用@Inherited
@Inherited元Annotation指定被他修饰的Annotation将具有继承性:如果某个类使用了A的Annotation(定义该Annotation时使用了@Inherited修饰)修饰,则其子类将自动具有A注释。
使用APT处理Annotation
APT是一种处理Annotation的工具,它对源代码文件进行检测找出其中的Annotation后,使用Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其他文件,APT还会编译生成的源代码文件和原来的源文件将它们一起生成class文件。
为了使用系统的APT工具来读取源文件中的Annotation,程序员必须自定义一个Annotation处理器,编写Annotation处理器需要使用JDK lib目录中的tools.jar里的四个包:
- com.sun.mirror.apt:和APT交互的接口
- com.sun.mirror.declaration:包含各种封装类成员、类方法、类声明的接口。
- com.sun.mirror.type 包含各种封装源代码中程序元素的接口。
- com.sun.mirror.util 提供了用于处理类型和声明的一些工具。