概念
注解,也称元数据,在Java SE5中引入,为我们在代码中添加信息提供了一种形式化的方法,使我们在稍后某个时刻非常方面地使用这些数据。
标准注解
标准注解指的是Java自带的三个注解:Override
、Deprecated
、SuppressWarnings
Override
表示当前方法覆盖超类中的方法
Deprecated
表示已经过时,不建议使用
SuppressWarnings
关闭不正当的编译器警告
元注解
所谓的元注解,就是用来定义注解的注解,即是我们可以利用元注解来自定义其他注解。元注解有 @Target
、@Retention
、@Document
、@Inherit
。
@Target
表示该注解用于什么地方. ElementType
参数包括:
CONSTRUCTOR
: 构造器的声明
FIELD
: 域声明(包括 enum)
LOCAL_VARIABLE
: 局部变量声明
METHOD
: 方法声明
PACKAGE
: 包声明
PARAMETER
: 参数声明
TYPE
: 类、接口(包括注解类型)或enum 声明
@Retention
表示需要在什么级别保存该注解信息。可选的 RetentionPolicy
参数包括:
SOURCE
:注解将被编译器丢弃
CLASS
:注解在 class 文件中可用,但会被 JVM 丢弃
RUNTIME
:JVM 运行时保留注解,可通过反射获取注解信息
@Document
将次注解包含在 JavaDoc 中
@Inherit
允许子类继承父类的注解
示例
1 2 3 4 5 6 7 8 9 10 11
|
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { int id();
String description() default "no description"; }
|
注解元素
从上面这个例子可以看出,定义注解有点类似定义接口,注解名类似接口名,注解元素类似方法。与接口定义不同,注解元素的类型有严格的限制,下面是注解元素的支持的类型。
- 所有基本类型
- String
- Class
- enum
- Annotation
- 以上类型的数组
默认值限制
编译器对注解元素的默认值比较挑剔,注解元素不能有不确定的值。意思是注解元素要么有默认值,要么使用注解时提供。
注解处理器
开头的时候我们就说了,「注解是为我们在代码中添加信息提供了一种形式化的方法,使我们在稍后某个时刻非常方面地使用这些数据。」,所以定义了注解,我们得想方法提取注解中的信息,为我们所用,因此需要写一个注解处理器。下面是从《Java 编程思想》摘取的例子,用于寻找缺失的用例。
1. 定义注解
1 2 3 4 5 6 7 8
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { int id();
String description() default "no description"; }
|
2. 使用注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class PasswordUtils {
@UseCase(id=47, description="validatePassword") public boolean validatePassword(String password) { return password.matches("\\w*\\d\\w*"); } @UseCase(id=48) public String encrytPassword(String password) { return new StringBuilder(password).reverse().toString(); } @UseCase(id=49, description="checkForNewPassword") public boolean checkForNewPassword(List<String> prevPasswords, String password) { return !prevPasswords.contains(password); } }
|
3. 注解处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class UseCaseTracker {
public static void trackUseCase(List<Integer> useCases, Class<?> cls) { for (Method method: cls.getDeclaredMethods()) { UseCase useCase = method.getAnnotation(UseCase.class); if (useCase != null) { System.out.println("Find UseCase:" + useCase.id() + ", " + useCase.description()); useCases.remove(new Integer(useCase.id())); } } for (int i: useCases) { System.out.println("Warning: missing the use case-" + i); } }
public static void main(String[] args) { List<Integer> usecases = new ArrayList<>(); Collections.addAll(usecases, 47, 48, 49, 50); trackUseCase(usecases, PasswordUtils.class); } }
|
运行结果:
1 2 3 4
| Find UseCase:47, validatePassword Find UseCase:48, no description Find UseCase:49, checkForNewPassword Warning: missing the use case-50
|
参考
《Java 编程思想》