注解
注解,也被称为元数据,为我们在代码中添加信息提供了一种形式化的方法,使我们可以在稍后某个时刻非常方便地使用这些数据。(Java编程思想)
目前很多框架开发或者Android开发中都用到了注解,SDK开发中也有很多可以对接口添加限制以规范用户使用的规则,这些都是值得我们去学习的。
关于注解的基础知识与android自定义注解本文就不做具体的介绍了,给大家推荐一篇博文如果有不了解android 自定义注解的同学可以先看看这篇文章。
android自定义注解
简单的例子
public class MyProcessor extends AbstractProcessor {
// 元素操作的辅助类
Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 获得被该注解声明的元素
Set<? extends Element> elememts = roundEnv.getElementsAnnotatedWith(Override.class);
// 遍历
for (Element ele : elememts) {
// 判断该元素是否为类
if (ele.getKind() == ElementKind.CLASS) {
//
} else if (ele.getKind() == ElementKind.FIELD) {
// 判断该元素是否为成员变量
}else if (ele.getKind() == ElementKind.METHOD) {
// 判断该元素是否为成员变量
}
else if (ele.getKind() == ElementKind.PARAMETER) {
// 判断该元素是否为成员变量
}
}
return true;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> set = super.getSupportedAnnotationTypes();
//返回改类可以处理的注解
set.add(Override.class.getCanonicalName());
return set;
}
}
如上MyProcessor 可以看成是一个编译时注解,当编译代码的时候所有被Override 注解的元素都会被传入到MyProcessor里面的process里面处理。
Element
1.1 基础
Represents a program element such as a package, class, or method. Each element represents a static, language-level construct (and not, for example, a runtime construct of the virtual machine)
即element是代表程序的一个元素,这个元素可以是:包、类/接口、属性变量、方法/方法形参、泛型参数。element是j编译时注解处理器技术的基础,因此如果要编写此类框架,熟悉element是必须的。
如上图所示,最终在编译的过程中我们的源码会被处理为一个Element 树,
Element 树就类似与HTML 里面的DOM数据,父节点可以获取到所有的子节点,有子节点的Element提供了getEnclosingElement 用于获取所有的子Elment;同理子节点也可以获取到父节点, 有父节点的Element提供了getEnclosedElements 用户获取父Elment。通过这两个方法我们就可以实现在Element 树上下移动,进而遍历整个Element树。
一个注解可以用来注解哪些元素在定义这个注解的时候已经规定好了。
可以被注解的元素都定义在ElementKind 里面,常见的ElementKind 如下
-
ElementKind.CLASS :
表示注解的是类,对应的具体的Element 为TypeElement
-
ElementKind.FIELD :
表示注解的是成员变量,对应的具体的Element 为VariableElement 。
-
ElementKind.ExecutableElement :
表示注解的是方法,对应的具体的Element 为ExecutableElement
TypeMirror
表示Java编程语言中的类型。类型包括基元类型、声明的类型(类和接口类型)、数组类型、类型变量和null类型。还表示了通配符类型参数、可执行文件的签名和返回类型,以及与包和关键字void相对应的伪类型。
我们举个例子来解释TypeMirror的作用于
public Activity testType(MessageBean message) {
//处理消息
}
假设有一个类有上面这个一个方法,通过上面我们知道一个方法实际就是一个ExecutableElement,现在我想获取到进入到这个方法的返回值对应的Elment 树也就是Activity 对应的Activity 数据,或者我想进入方法的第一个参数的Element数据。
getReturnType 返回的是一个TypeMirror 而不是一个Elment ,因此我们无法直接获取对应的Element。需要通过TypeMirror 的Element asElement() 方法进入到对应的Element 树。
那么为什么这么设计呢?大概跟泛型有写关联
如上假如getReturnType 直接返回的就是HashMap 对应的Element 树,那么我们就只能获取到定义HashMap 的时候泛型名字也即是K,V ,想要获取testType 返回的HashMap 泛型具体类型是无法做到的,因此之类通过TypeMirror 中转了一次。
testType 对应Element的getReturnType 返回的实际类型是DeclaredType,通过getTypeArguments 方法就可以获取到泛型的真实类型,这里就是String。
常见的TypeMirror 如下:
PrimitiveType: 表示基本类型,例如int,float 等
DeclaredType: 表示类,枚举等类型
TypeVariable:表示泛型,例如 K extend String
.关于更多的TyoeMirror 大家可以自己查阅资料。
泛型Type
Java反射里面也存在一个Type 概念。
注意这个Type 与TypeMirror 区别,Type 主要是应用于反射。
Method method = new Main().getClass().getMethod("test", List.class);
Type[] types = method.getGenericParameterTypes();
常见的Type 子类有:
-
TypeVariable:
表示类型变量,一般用大写字母作为变量,比如K,V
-
ParameterizedType:
表示带有参数的Type,直白讲就是形如 A 这种类型的Type,A表示类的名字,T是A类上定义的泛型。
-
WildcardType:
表示具有通配符的泛型,例如 List<? extends Number>是ParameterizedType ,而? extends Number 就是WildcardType。
- GenericArrayType:
泛型数组,描述的是ParameterizedType类型以及TypeVariable类型数组,即形如:
List<T>[][]、T[]等。
- Class:
-
类类型如String,class
不同类型的Type 有不同的方法用于解析对应的Type 数据,例如A 是一个ParameterizedType ,此时想要获取A具体的类型就可以调用Type getRawType()方法,前面说了Class 实现了ype 接口,此处实际返回的就是A类的Class。有感兴趣的同学可以自行研究其他的API。
反射中的Type 与编译时注解没有什么关系,放到这是希望大家将之与TypeMirror 区分开来,对比学习。
总结
我们下一篇就进入EventBus 的编译时注解,结合这个具体的例子研究一下编译时注解与动态生成代码。