本文内容主要摘录自《Java编程思想》
注解是为了在代码中添加信息的一种方式,可以使我们在之后的某个时刻获取并使用这些数据。
Java中的注解
Java目前内置了三种标准注解和四种元注解。
标准注解就是说可以直接在代码中使用的,并且有约定好含义的注解。定义在Java中的三种元注解如下:
- @Override,表示当前的方法定义将覆盖父类中的方法。如果你不小心拼写错误,或者方法参数不对,那么编译器就会发出错误提示。
- @Deprecated,表示该方法是过时的,不建议使用。如果你坚持要使用的话,编译器会发出警告信息。
- @SuppressWarnings,编译器不会对使用该注解的方法发出任何警告信息。
Java中的元注解有4个。元注解是用来定义、生成其他注解的:
@Target
表示该注解可以用在什么地方。可能的ElementType参数包括:
- TYPE:类,接口(包括注解类)或枚举类声明。
- FIELD:域声明(包括枚举对象)
- METHOD:方法声明
- PARAMETER:参数声明
- CONSTRUCTOR:构造器声明
- LOCAL_VARIABLE:局部变量声明
- ANNOTATION_TYPE:注解类型声明
- PACKAGE:包声明
@Retention
表示需要在什么级别保存该注解信息。可选的Retention参数包括:
- SOURCE:注解信息在编译阶段就会被丢弃
- CLASS:注解信息保留在class文件中,但是会被VM丢弃。
- RUNTIME:VM在运行期也会保留注解,因此可以通过反射机制读取注解信息。
@Documented
将此注解包含在Javadoc中。
@Inherited
允许子类继承父类中的注解。
自定义注解
既然元注解是生成注解的注解,那么元注解又是怎么生成的呢?当然还是用元注解!我们来看一下元注解@Documented的源码:
1 |
|
可以看到,这里使用了三个元注解来生成了@Documented这个元注解。除了@
符号外,定义一个注解很像定义一个空接口。一般来说,注解中都会包含一些元素来表示某些值,这样在分析处理注解时,就能使用这些值,但是也有像@Documented这种没有元素的注解,称之为标记注解。
下面我们定义一个有元素的注解:
1 | (ElementType.METHOD) |
注解@UseCase
包含int元素id,以及一个String元素description,注解元素可用类型如下所示:
- 所有的基本类型(int,float, boolean等)
- String
- Class
- enum
- Annotation
- 以上类型的数组
注意到元素description
含有一个默认值"no description"
,一般来说,对于非基类型的元素,无论是在源码中声明,还是在注解接口中定义默认值时,都不能用null
作为期默认值。一般用一些特殊的值表示其不存在,比如空字符串或负数。比如:
1 | (ElementType.METHOD) |
使用自定义注解
下面,我们就看看如何在实际的代码中使用@UseCase
注解:
1 | public class PasswordUtils { |
注解的元素在使用时表现为键值对的形势,并且需要置于@UseCase
声明之后的括号内。
编写注解处理器
1 | public class UseCaseTracker { |
运行结果如下:
1 | Found Use Case: 49 no description |
这个程序用到了两个反射的方法:getDeclaredMethods()和getAnnotation(),他们都属于AnnotatedElement接口(Class, Method与Field等类都实现了该接口)。getAnnotation()方法返回制定类型的注解对象,在这里就是UseCase。如果被注解的方法上没有该类型的注解,则返回null值。然后我们通过调用id()和description()方法从返回的UseCase对象中提取元素的值。其中,encriptPassword()方法在注解的时候没有指定description的值,因此通过description()方法取得的是默认值no description。