14.2 JDK的元注解

14.2 JDK的元注解

JDK除在java.lang下提供了5个基本的注解之外,还在java.lang.annotation包下提供了6个Meta注解(元注解,其中有5个元注解都用于修饰其他的注解定义。其中@Repeatable专门用于定义Java8新增的重复注解,本章后面会重点介绍相关内容。此处先介绍常用的4个元注解。

java.lang.annotation包中用于修饰其他注解定义的注解

  • @Retention
  • @Target
  • @Documented
  • @Inherited

14.2.1 使用@Retention

@Retention只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值。

value成员变量的值只能是如下三个

属性 描述
RetentionPolicy.CLASS 编译器将把注解记录在class文件中。当运行Java程序时,JVM不可获取注解信息。这是默认值。
RetentionPolicy.RUNTIME 编译器将把注解记录在class文件中。当运行Java程序时,JVM也可获取注解信息,程序可以通过反射获取该注解信息。
RetentionPolicy.SOURCE 注解只保留在源代码中,编译器直接丢弃这种注解。

如果需要通过反射获取注解信息,就需要使用value属性值为RetentionPolicy.RUNTIME@Retention。使用@Retention元注解可采用如下代码为value指定值

1
2
3
定义下面的 @Testable注解保留到运行时
@Retention(value= RetentionPolicy.RUNTIME)
public @interface Testable{}

也可采用如下代码来为value指定值。

1
2
3
//定义下面的 @Testable注解将被编译器直接丢弃
@Retention(RetentionPolicy.SOURCE)
public @interface Testables{}

当注解的成员变量名为value时可以省略value

上面代码中使用@Retention元注解时,并未通过value=RetentionPolicy.SOURCE的方式来为该成员变量指定值,这是因为当注解的成员变量名为value时,程序中可以直接在注解后的括号里指定该成员变量的值,无须使用name=Value的形式

如果使用注解时只需要为value成员变量指定值,则使用该注解时可以直接在该注解后的括号里指定value成员变量的值,无须使用“value=变量值”的形式

14.2.2 使用@Target

Target也只能修饰注解定义,它用于指定被修饰的注解能用于修饰哪些程序单元@Target元注解也包含一个名为value的成员变量,该成员变量的值只能是如下几个。

属性 描述
ElementType.ANNOTATION_TYPE 指定该策略的注解只能修饰注解。
ElementType.CONSTRUCTOR 指定该策略的注解只能修饰构造器
ElementType.FIELD 指定该策略的注解只能修饰成员变量。
ElementType.LOCAL_VARIABLE 指定该策略的注解只能修饰局部变量
ElementType.METHOD 指定该策略的注解只能修饰方法定义
ElementType.PACKAGE 指定该策略的注解只能修饰包定义。
ElementType.PARAMETER 指定该策略的注解可以修饰参数。
ElementType.TYPE 指定该策略的注解可以修饰类、接口(包括注解类型)或枚举定义。

与使用@Retention类似的是,使用@Target也可以直接在括号里指定value值,而无须使用name=Value的形式。

代码 指定一个注解只能修饰成员变量

如下代码指定@ActionListenerFor注解只能修饰成员变量。

1
2
@Target(ElementType.FIELD)
public @interface ActionListenerFor{}

代码 指定一个注解只能修饰方法

如下代码片段指定@Testable注解只能修饰方法。

1
2
@Target(ElementType.METHOD);
public @interface Testable{}

14.2.3 使用@Documented

@Documented用于指定被@Documented注解修饰的注解类将被javadoc工具提取成文档,如果定义注解类时使用了@Documented修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解说明。

程序

下面代码定义了一个Testable注解,程序使用 @Documented来修饰tEstable注解定义,所以该注解将被javadoc工具所提取。

1
2
3
4
5
6
7
8
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
// 定义Testable注解将被javadoc工具提取
@Documented
public @interface Testable {
}

上面代码中的粗体字代码指定了javadoc工具生成的API文档将提取@Testable的使用信息下面代码定义了一个MyTest类,该类中的inf()方法使用了@Testable修饰。

1
2
3
4
5
6
7
public class MyTest {
// 使用@Test修饰info方法
@Testable
public void info() {
System.out.println("info方法...");
}
}

使用javadoc工具为Testable.javaMyTest.java文件生成API文档

1
javadoc Testable.java MyTest.java -d api

效果如图14.1所示。

如果把上面Testable.java程序中的注解@Documented删除或注释掉,再次使用javadoc工具生成的API文档如图14.2所示

对比图14.1和142所示两份API文档中灰色区域覆盖的info()方法说明,图1411中的info()方法说明里包含了@Testable的信息,这就是使用@DocumentedAnnotation的作用

14.2.4 使用@Inherited

@Inherited元注解指定被它修饰的注解将具有继承性:如果某个类使用了@Xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@Xxx修饰。

程序

下面使用 @Inherited元注解修饰@Inheritable定义,则该注解将具有继承性。

1
2
3
4
5
6
7
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Inheritable {
}

上面程序表明@Inheritable具有继承性,如果某个类使用了@Inheritable修饰,则该类的子类将自动使用 @Inheritable修饰。

下面程序中定义了一个Base基类,该基类使用了 @Inheritable修饰,则Base类的子类将会默认使用 @Inheritable修饰。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 使用@Inheritable修饰的Base类
@Inheritable
class Base {
}

// TestInheritable类只是继承了Base类,
// 并未直接使用@Inheritable Annotiation修饰
public class InheritableTest extends Base {
public static void main(String[] args) {
// 打印TestInheritable类是否具有@Inheritable修饰
System.out.println(InheritableTest.class.isAnnotationPresent(Inheritable.class));
}
}

上面程序中的Base类使用了@Inheritable修饰,而该注解具有继承性,所以其子类也将自动使用@Inheritable修饰。运行上面程序,会看到输出:true
如果将InheritableTest.java程序中的粗体字代码注释掉或者删除,将会导致@Inheritable不具有继承性。运行上面程序,将看到输出:false