夏眠鱼

Jul 18, 2016

Java 注解

概念

注解,也称元数据,在Java SE5中引入,为我们在代码中添加信息提供了一种形式化的方法,使我们在稍后某个时刻非常方面地使用这些数据。

标准注解

标准注解指的是Java自带的三个注解:OverrideDeprecatedSuppressWarnings

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 {

/**
* 注解处理方法,通过反射获取到注解信息
*
* @param useCases
* @param cls
*/
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 编程思想》

OLDER > < NEWER