Android⾃定义注解(Annotation)
在市⾯上很多框架都有使⽤到注解,⽐如butterknife库、EventBus库、Retrofit库等等。也是⼀直好奇他们都是怎么做到的,注解的⼯作原理是啥。咱们能不能⾃⼰去实现⼀个简单的注解呢。
注解(Annotation)是JDK1.5新增加功能,注解其实就是添加在类、变量、⽅法、参数等前⾯的⼀个修饰符⼀个标记⽽已(不要把他想的太复杂)。⽐如下⾯的代码⾥⾯@Override、@IdRes就是注解。
@Override
public <T extends View> T findViewById(@IdRes int id) {
return getDelegate().findViewById(id);
}
上⾯我们强调了注解就是⼀个修饰符⼀个标记⽽且。但是通过注解能做的事情确是⽆穷。在代码编译或者运⾏的过程中我们可以到这些 注解,在到这些注解之后咱们就可以做很多事情了,⽐如⾃动做⼀些代码处理(赋值、检测、调⽤等等)或者⼲脆⽣成⼀些额外的java⽂件等。下⾯会⽤更加具体的实例来说明。
注解的作⽤:简化代码,提⾼开发效率。
注意哦,肯定是能提⾼代码开发效率,并不⼀定能提供程序运⾏效率。
接下来我们通过学习⾃定义注解(定义我们⾃⼰的注解)来让⼤家对注解有⼀个深刻的认识。
⼀、元注解
在我们⾃定义注解之前我们需要来先了解下元注解。元注解是⽤来定义其他注解的注解(在⾃定义注解的时候,需要使⽤到元注解来定义我们的注解)。java.lang.annotation提供了四种元注解:@Retention、 @Target、@Inherited、@Documented。
元注解是⽤来修饰注解的注解。在⾃定义注解的时候我们肯定都是要⽤到元注解的。因为我们需要定义我们注解的是⽅法还是变量,注解的存活时间等等。
元注解说明
@Target表明我们注解可以出现的地⽅。是⼀个ElementType枚举
@Retention这个注解的的存活时间
@Document表明注解可以被javadoc此类的⼯具⽂档化
@Inherited是否允许⼦类继承该注解,默认为false
1.1、@Target
@Target元注解⽤来表明我们注解可以出现的地⽅,参数是⼀个ElementType类型的数组,所以@Target可以设置注解同时出现在多个地⽅。⽐如既可以出现来类的前⾯也可以出现在变量的前⾯。
@Target元注解ElementType枚举(⽤来指定注解可以出现的地⽅):
@Target-ElementType类型说明
ElementType.TYPE接⼝、类、枚举、注解
ElementType.FIELD字段、枚举的常量
ElementType.METHOD⽅法
ElementType.PARAMETER⽅法参数
ElementType.CONSTRUCTOR构造函数
ElementType.CONSTRUCTOR构造函数
@Target-ElementType类型说明
ElementType.LOCAL_VARIABLE局部变量
ElementType.ANNOTATION_TYPE注解
ElementType.PACKAGE包
1.2、@Retention
@Retention表⽰需要在什么级别保存该注释信息,⽤于描述注解的⽣命周期(即:被描述的注解在什么范围内有效)。参数是RetentionPolicy枚举对象。
RetentionPolicy的枚举类型有(默认值为CLASS.):
@Retention-RetentionPolicy类型说明
RetentionPolicy.SOURCE注解只保留在源⽂件,当Java⽂件编译成class⽂件的时候,注解被遗弃
RetentionPolicy.CLASS注解被保留到class⽂件,但jvm加载class⽂件时候被遗弃,这是默认的⽣命周期
RetentionPolicy.RUNTIME注解不仅被保存到class⽂件中,jvm加载class⽂件之后,仍然存在
SOURCE < CLASS < RUNTIME,前者能作⽤的地⽅后者⼀定也能作⽤.
1.3、@Document
@Document表明我们标记的注解可以被javadoc此类的⼯具⽂档化。
1.4、@Inherited
@Inherited表明我们标记的注解是被继承的。⽐如,如果⼀个⽗类使⽤了@Inherited修饰的注解,则允许⼦类继承该⽗类的注解。
⼆、⾃定义注解
2.1、⾃定义运⾏时注解
运⾏时注解:在代码运⾏的过程中通过反射机制到我们⾃定义的注解,然后做相应的事情。
反射:对于任意⼀个类,都能够知道这个类的所有属性和⽅法;对于任意⼀个对象,都能够调⽤它的任意⼀个⽅法和属性。
⾃定义运⾏是注解⼤的⽅⾯分为两步:⼀个是申明注解、第⼆个是解析注解。
2.1.1、申明注解
申明注解步骤:
1. 通过@Retention(RetentionPolicy.RUNTIME)元注解确定我们注解是在运⾏的时候使⽤。
2. 通过@Target确定我们注解是作⽤在什么上⾯的(变量、函数、类等)。
3. 确定我们注解需要的参数。
⽐如下⾯⼀段代码我们声明了⼀个作⽤在变量上的BindString运⾏时注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindString {
int value();
android retrofit}
2.1.2、注解解析
运⾏时注解的解析我们简单的分为三个步骤:
1. 到类对应的所有属性或者⽅法(⾄于是类的属性还是⽅法就要看我⾃定义的注解是定义⽅法上还是属性上了)。
2. 到添加了我们注解的属性或者⽅法。
3. 做我们注解需要⾃定义的⼀些操作。
2.1.2.1、获取类的属性和⽅法
既然注解是我们⾃定义的,我肯定事先会确定我们注解是加在属性上的还是加在⽅法上的。
通过Class对象我们就可以很容易的获取到当前类⾥⾯所有的⽅法和属性了:
Class类⾥⾯常⽤⽅法介绍(这⾥我们不仅仅介绍了获取属性和⽅法的,还介绍了⼀些其他Class⾥⾯常⽤的⽅法)
/**
* 包名加类名
*/
public String getName();
/**
* 类名
*/
public String getSimpleName();
/**
* 返回当前类和⽗类层次的public构造⽅法
*/
public Constructor<?>[] getConstructors();
/**
* 返回当前类所有的构造⽅法(public、private和protected)
* 不包括⽗类
*/
public Constructor<?>[] getDeclaredConstructors();
/**
* 返回当前类所有public的字段,包括⽗类
*/
public Field[] getFields();
/
**
* 返回当前类所有申明的字段,即包括public、private和protected,
* 不包括⽗类
*/
public native Field[] getDeclaredFields();
/**
* 返回当前类所有public的⽅法,包括⽗类
*/
*/
public Method[] getMethods();
/**
* 返回当前类所有的⽅法,即包括public、private和protected,
* 不包括⽗类
*/
public Method[] getDeclaredMethods();
/**
* 获取局部或匿名内部类在定义时所在的⽅法
*/
public Method getEnclosingMethod();
/**
* 获取当前类的包
*/
public Package getPackage();
/**
* 获取当前类的包名
*/
public String getPackageName$();
/**
* 获取当前类的直接超类的 Type
*/
public Type getGenericSuperclass();
/**
* 返回当前类直接实现的接⼝.不包含泛型参数信息
*/
public Class<?>[] getInterfaces();
/**
* 返回当前类的修饰符,public,private,protected
*/
public int getModifiers();
类⾥⾯每个属性对应⼀个对象Field,每个⽅法对应⼀个对象Method。
2.1.2.2、到添加注解的属性或者⽅法
上⾯说道每个属性对应Field,每个⽅法对应Method。⽽且Field和Method都实现了AnnotatedElement接⼝。都有AnnotatedElement接了我们就可以很容易的到添加了我们指定注解的⽅法或者属性了。
AnnotatedElement接⼝常⽤⽅法如下:
/**
* 指定类型的注释是否存在于此元素上
*/
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
/**
* 返回该元素上存在的指定类型的注解
*/
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
/**
* 返回该元素上存在的所有注解
*/
Annotation[] getAnnotations();
/**
* 返回该元素指定类型的注解
*/
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
DirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释(⽗类⾥⾯的不算)
*/
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
/**
* 返回直接存在该元素岸上某类型的注释
*/
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
DirectOrIndirectAnnotationsByType(this, annotationClass);
}
/**
* 返回直接存在与该元素上的所有注释
*/
Annotation[] getDeclaredAnnotations();
2.1.2.3、做⾃定义注解需要做的事情
添加了我们注解的属性或者⽅法已经拿到了,之后要做的就是⾃定义注解⾃定义的⼀些事情了。⽐如在某些特定条件下⾃动去执⾏我们添加注解的⽅法。下⾯我们也会⽤两个具体的实例来说明。
2.1.3、运⾏时注解实例
我们通过两个简单的实例来看下⾃定义运⾏时注解是怎么操作的。
2.1.
3.1、通过注解⾃动创建对象
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论