10.注解(Thinking in java学习十)
2020-04-16 本文已影响0人
Vinson武
基本概念
-
定义:注解(也称为元数据)为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。
-
注解在一定程度上是在把元数据和元代码文件结合在一起。通过使用注解,我们可以把这些元数据保存在Java源代码中,并利用annotation API为自己的注解构造处理工具。
-
Java内置了三种定义在java.lang中的注解
- @Override,覆盖超类
- @Deprecated,过期注释
- SuppressWarnings,关闭不当的编译器的警告信息。
另外还提供了==四种元注解==,专门负责新注解的创建。
- @Targer:表示该注解可以用于什么地方。
- @Retention:表示需要在什么级别保存该注解信息。
- @Documented:将此注解包含在Javadoc中。
- @Inherited:允许子类继承父类中的注解。
@Targer
image.png
@Retention
image.png
基本语法
定义注解
@Targer(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UserCase{
//注解元素
public int id();
public String desc() default "no desc"; //默认值
}
注解的定义看起来很像接口的定义,注解也将会编译成class文件。
注解的元素看起来就像接口的方法,唯一的区别是你可以为其指定默认值。
没有元素的注解称为标记注解。
注解使用
public class Test{
@UserCase(id = 47, desc = "xxxx")
public boolean testMet(String str){
...
return true;
}
}
注解的元素在使用时表现为名-值对的形式,并需要置于@UserCase声明之后的括号内。
编写注解处理器
注解元素
注解元素的类型有以下几种:
- 所有基本类型(int, float等)
- String
- Class
- enum
- Annotation(注解)
- 以上类型的数组
如果使用其他类型,编译器会报错。注意,也不能使用包装类型。
默认值限制
编译器对注解元素默认值要求较高
- 首先,元素不能有不确定的值。即元素要么具有默认值,要么在使用注解时提供元素的值。
- 其次,对于非基本类型的元素,定义默认值都不能以null作为其值。(这个约束使得处理器很难表现一个元素的存在或缺失,只能自己定义一些特殊值,比如空字符串或负数来表示元素不存在)
注解不支持继承,不能使用关键字extends来继承某个@interface。
实现处理器
一个简单的原始注解处理器
public class UseCaseTraker{
public static void useCaseTrakers(List<Integer> useCases, Class<?>cl){
for(Method m : cl.getDeclaredMethods()){
UseCase uc = m.getAnnotation(UseCase.class);
if(uc !=null){
System.out.println(uc.id() + " " + uc.desc());
useCase.remove(new Integer(uc.id()));
}
}
for(int i:useCase){
System.out.println("current case " + i);
}
}
public static void main(String[] args){
List<Integer>useCase = new ArrrayList<Integer>();
Collections.addAll(useCase, 47, 48);
trackUseCase(useCase, Test.class);
}
}
//输出: 47 xxxx
//current case 48
getAnnotation(Class<A> annotationClass)获得注解
getDeclaredAnnotations()获得注解元素
Java自定义注解是通过运行时靠反射获取注解
处理器的写法有固定的套路,继承AbstractProcessor。
public class MyProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
//当我们编译程序时注解处理器工具会调用此方法并且提供实现ProcessingEnvironment接口的对象作为参数。
super.init(processingEnv);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(UserCase.class.getCanonicalName());//返回该注解处理器支持的注解集合
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
//处理注解
return true;
}
}
- init(ProcessingEnvironment processingEnv) 被注解处理工具调用,参数ProcessingEnvironment 提供了Element,Filer,Messager等工具
- getSupportedAnnotationTypes() 指定注解处理器是注册给那一个注解的,它是一个字符串的集合,意味着可以支持多个类型的注解,并且字符串是合法全名。
- getSupportedSourceVersion 指定Java版本
- process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) 这个也是最主要的,在这里扫描和处理你的注解并生成Java代码,信息都在参数RoundEnvironment 里了。
注册注解处理器
image.png
google提供了一个注册处理器的库
compile 'com.google.auto.service:auto-service:1.0-rc2'
一个注解搞定:
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
...
}