22.3 创建注解
22.3 创建注解
框架和库是怎么实现注解的呢?我们来看注解的创建。
我们通过一些例子来说明,先看@Override
的定义:
1 |
|
定义注解与定义接口有点类似,都用了interface,不过注解的interface前多了@。另外,它还有两个元注解@Target
和@Retention
,这两个注解专门用于定义注解本身。@Target
表示注解的目标,@Override
的目标是方法(ElementType.METHOD)。ElementType是一个枚举,主要可选值有:
- TYPE:表示类、接口(包括注解),或者枚举声明;
- FIELD:字段,包括枚举常量;
- METHOD:方法;
- PARAMETER:方法中的参数;
- CONSTRUCTOR:构造方法;
- LOCAL_VARIABLE:本地变量;
- MODULE:模块(Java 9引入的)。
目标可以有多个,用{}表示,比如@SuppressWarnings
的@Target
就有多个。Java 7的定义为:
1 |
|
如果没有声明@Target
,默认为适用于所有类型。
@Retention
表示注解信息保留到什么时候,取值只能有一个,类型为RetentionPolicy,它是一个枚举,有三个取值。
- SOURCE:只在源代码中保留,编译器将代码编译为字节码文件后就会丢掉。
- CLASS:保留到字节码文件中,但Java虚拟机将class文件加载到内存时不一定会在内存中保留。
- RUNTIME:一直保留到运行时。
如果没有声明@Retention,则默认为CLASS。
@Override和@SuppressWarnings都是给编译器用的,所以@Retention都是Retention-Policy.SOURCE。
可以为注解定义一些参数,定义的方式是在注解内定义一些方法,比如@Suppress-Warnings
内定义的方法value,返回值类型表示参数的类型,这里是String[]。使用@Suppress-Warnings
时必须给value提供值,比如:
1 |
当只有一个参数,且名称为value时,提供参数值时可以省略”value=”,即上面的代码可以简写为:
1 |
注解内参数的类型不是什么都可以的,合法的类型有基本类型、String、Class、枚举、注解,以及这些类型的数组。
参数定义时可以使用default指定一个默认值,比如,Guice中Inject注解的定义:
1 |
|
它有一个参数optional,默认值为false。如果类型为String,默认值可以为””,但不能为null。如果定义了参数且没有提供默认值,在使用注解时必须提供具体的值,不能为null。
@Inject多了一个元注解@Documented,它表示注解信息包含到生成的文档中。
与接口和类不同,注解不能继承。不过注解有一个与继承有关的元注解@Inherited
,它是什么意思呢?我们看个例子:
1 | public class InheritDemo { |
Test是一个注解,类Base有该注解,Child继承了Base但没有声明该注解。main方法检查Child类是否有Test注解,输出为true,这是因为Test有注解@Inherited
,如果去掉,输出会变成false。