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 提供了用于处理类型和声明的一些工具。