Java 注解
注解(Annotation)就像一个标签,用于为 Java 代码提供元数据。作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。
JDK预置注解包括:
@Deprecated,即将废弃的标记;
@Override,表示当前的方法定义将覆盖超类中的方法;
@SuppressWarnings,阻止警告的意思
@SafeVarargs,参数安全类型注解
@FunctionalInterface,函数式接口注解
元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其它的注解上面。
@Retention,注解的存活时间,取值如下:
- RetentionPolicy.SOURCE,注解只在源码阶段保留,将被编译器丢弃;
- RetentionPolicy.CLASS,注解保留到编译期间,将被VM丢弃;
- RetentionPolicy.RUNTIME,注解保留到运行期间,因此可以通过反射机制读取注解的信息;
@Target,限定注解应用的场景,注解可以加在包、类,属性、方法,方法的参数以及局部变量上,取值如下:
- ElementType.ANNOTATION_TYPE,可以给一个注解进行注解
- ElementType.CONSTRUCTOR,可以给构造方法进行注解
- ElementType.FIELD,可以给属性进行注解
- ElementType.LOCAL_VARIABLE,可以给局部变量进行注解
- ElementType.METHOD,可以给方法进行注解
- ElementType.PACKAGE,可以给一个包进行注解
- ElementType.PARAMETER,可以给一个方法内的参数进行注解
- ElementType.TYPE,可以给一个类型进行注解,比如类、接口、枚举
@Repeatable,注解可以被多次应用
@Inherited,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。
@Documented,将注解中的元素包含到Javadoc中;
注解定义
在定义注解时,不能继承其他的注解或接口。@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum、Annotation)。可以通过default来声明参数的默认值。
一个例子,注解定义和应用
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
int id();
String description() default "no description";
}
public class PasswordUtils {
@UseCase(id = 47, description = "password must contain at lease one numeric")
public boolean validatePassword(String password) {
return password.matches("\\w*\\d\\w*");
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
@UseCase(id = 49, description = "new passwords can't equal previously used ones")
public boolean checkForNewPassword(List<String> prevPasswords, String password) {
return !prevPasswords.contains(password);
}
}
测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class UseCaseTracker {
@Autowired
private PasswordUtils passwordUtils;
public static void trackUseCases(List<Integer> useCases, Class<?> cl) {
for (Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if (uc != null) {
System.out.println("Found Use Case: " + uc.id() + " " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for (int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
@Test
public void test() {
List<Integer> useCases = new ArrayList<>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
}
输出
Found Use Case: 48 no description
Found Use Case: 49 new passwords can't equal previously used ones
Found Use Case: 47 password must contain at lease one numeric
Warning: Missing use case-50
上面用到了两个反射方法:getDeclaredMethods() 和 getAnnotation(),它们都属于AnnotatedElement接口(Class, Mehod, Filed 等类都实现了该接口);
getAnnotation() 方法返回指定类型的注解对象,在这里就是UseCase。如果被注解的方法上没有该类型的注解,则返回null值。
然后我们通过调用id()和 description() 方法从返回的UseCase对象中提取元素的值。其中,encryptPassword()方法在注解的时候没有指定description的值,因此取到的是默认值。
关于注解元素的默认值:
- 注解的元素可以有默认值,但不能有不确定的值,也就是说,元素要么具有默认值,要么在使用注解时提供元素的值。
- 对于非基本类型的元素,无论是在源代码中声明时,或是在注解接口中定义默认值时,都不能以null作为其值,这个约束使得很难表现一个元素的存在或缺失状态,因为在每个注解的声明中,所有的元素都存在,并且都具有相应的值。为了绕开这个约束,只能自定义一些特殊的值来表示元素不存在,例如:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
int id() default -1;
String description() default "";
}
来源:oschina
链接:https://my.oschina.net/u/4263001/blog/4300960