Android注解知识点梳理-基础理论
目的
学完本文章你将会明白什么是Annotation,Annotation的基础声明与使用。
什么是注解?
要回答这个问题,我们先来看一个案例。
@Override
public String toString() {
return "This is String.";
}
在上面代码中:@Override 就是一个注解。
那么我现在来回答你什么是注解;注解是从Java1.5版本开始加入Java大家族的,目的是给代码提供一定的注释、标注。Annotation是一种应用于类、方法、参数、变量、构造器及包声明中的特殊修饰符。它是一种由JSR-175标准选择用来描述元数据的一种工具。
起初Annotation的出现是为了简化代码的XML配置与描述,用于简化这一过程。
除了@Override这一注解之外,我们常用的还有:
-
@Override
-
@Deprecated
-
SuppressWarnings
以上3中是咱们代码中最常见的3中基础注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Override 是为了标注方法是复写父类或是实现的接口,那么编译器会为我们在编译时做检测,检查是否在父类或集成的接口中有该方法,已达到避免出错的目的。上面的代码如果我们不加@Override然后改成 toString(int a) 也是可以编译过,但是这就与我们的初心违背了。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
@Deprecated是不受待见的,因为它的出现意味着类或方法是过时的,将要废弃的;我们的代码应当给具有更加优秀的方案替换的老旧的类或方法加上这样的注解,已表明这方法是过时的,使用者应当使用更好的方案。
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
@SuppressWarnings与上面2者基础注解不同,他具有一个接收值;该值是一个数组。主要的包括:
- all,抑制所有警告
- boxing,抑制与封装/拆装作业相关的警告
- cast,抑制与强制转型作业相关的警告
- dep-ann,抑制与淘汰注释相关的警告
- deprecation,抑制与淘汰的相关警告
- fallthrough,抑制与switch陈述式中遗漏break相关的警告
- finally,抑制与未传回finally区块相关的警告
- hiding,抑制与隐藏变数的区域变数相关的警告
- incomplete-switch,抑制与switch陈述式(enum case)中遗漏项目相关的警告
- javadoc,抑制与javadoc相关的警告
- nls,抑制与非nls字串文字相关的警告
- null,抑制与空值分析相关的警告
- rawtypes,抑制与使用raw类型相关的警告
- resource,抑制与使用Closeable类型的资源相关的警告
- restriction,抑制与使用不建议或禁止参照相关的警告
- serial,抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告
- static-access,抑制与静态存取不正确相关的警告
- static-method,抑制与可能宣告为static的方法相关的警告
- super,抑制与置换方法相关但不含super呼叫的警告
- synthetic-access,抑制与内部类别的存取未最佳化相关的警告
- sync-override,抑制因为置换同步方法而遗漏同步化的警告
- unchecked,抑制与未检查的作业相关的警告
- unqualified-field-access,抑制与栏位存取不合格相关的警告
- unused,抑制与未用的程式码及停用的程式码相关的警告
注解的作用
一般而言注解具有如下作用:
- 纯粹的标示、注释。比如给你的代码加上一个自定义的注解:@QIUJUER;这个注解有意义么?有意义,意义就在于表明这个代码是我写的;这个解释我服!!
- 生成代码文档,用于辅助代码文档生产,以及编译工具的查阅。例:
/
**
* @param count Count
* @return String
* @see #start(int)
* @since 1.2.0
*/
public String start(int count) {
return null;
}
-
实现代码跟踪,简化配置文件,简化XML文件的使用。长用于Web开发。
-
编译时进行代码跟踪,比如@Override
-
编译时进行辅助代码生成;如注入框架Butterknife,数据库框架DbFlow
-
运行时进行逻辑替换或叫做功能完善;比如Retrofit
注解的语法
上面简单的介绍了注解,而且明白了什么是注解,那么注解该如何声明?
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* @author qiujuer Email:qiujuer@live.cn
* @version 1.0.0
*/
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface JumpUiThread {
JumpType value() default JumpType.AUTO;
}
以上的代码展示了一个完整的自定义注解声明,现在我们来剖析一下。
注解的声明非常简单与接口的方式类似,只不过在interface前面加上一个 “@” 符号即可。至于注解中的JumpType这个仅仅是一个枚举而已,随后将会讲到。
元注解
这个名字很特别,在开篇的时候我们讲了Java的基础注解,OK记住哪些都是 基础注解 ,是Java基础的时候提供的;那么何为元注解?
“用于注解其他注解的注解叫做元注解。” 他的本质也是注解,只不过相对特殊是用于注解其他的注解而已。
- @Documented:注解是否将包含在JavaDoc中
- @Retention:什么时候使用该注解
- @Target:注解用于什么地方
- @Inherited:是否允许子类继承该注解
- @Repeatable:Java8引入,用于标示可以重复使用的注解
上面的5种注解都是元注解,也都是Java内置具备的;其中最后一项是Java8才加入的。在上面的例子中我们展示了其中3种元注解;下面我们来详细介绍一下。
@Documented
使用@Documented修饰的注解将会在JavaDoc输出的时候保留该注解。一般用于需要让阅读文档的人知道有这个注解,或者是这个注解需要让接手文档的人知晓;注解对逻辑流程具有一定的作用或干扰性。
public class Test {
@Documented
public @interface Annotation1 {
}
public @interface Annotation2 {
}
@Annotation1
public static class Foo1 {
}
@Annotation2
public static class Foo2 {
}
}
对于这个例子我们使用命令 “javadoc Test.java" 生成文档。
可以发现Foo1和Foo2文档并不相同,一个带有注解信息,一个未带有。
@Retention
注解保留到何时,用于约束注解的生命周期,有如下3种选择(枚举):
- RetentionPolicy.SOURCE:注解仅仅保留在源码级别,编译后class文件中将去除。一般用于编译时校验和代码生产使用。
- RetentionPolicy.CLASS:注解保留到class级别,但是JVM加载时将忽略不加载相关信息。常见的:@Override、@Deprecated、@SuppressWarnning,以上注解在你依赖一个已编译好的lib时依然生效。
- RetentionPolicy.RUNTIME:注解保留到运行时态,此时JVM会加载注解相关信息;你可以在运行时通过代码获取class type信息拿到。常见的如:运行时数据库框架或注入框架。
@Target
注解可以注解的类型,用于约束注解可以应用的地方(方法、参数等)。其枚举ElementType类型如下:
- ANNOTATION_TYPE 适用于注解类型声明(元注解就是很好的例子)
- CONSTRUCTOR 适用于构造函数声明
- FIELD 适用于字段声明(包括枚举常数)(常用)
- LOCAL_VARIABLE 适用于局部变量声明(常用)
- METHOD 适用于方法声明(常用)
- PACKAGE 适用于包(package)声明
- PARAMETER 适用于方法参数声明(常用)
- TYPE 适用于类,接口(包括注释类型)或枚举声明(常用)
- TYPE_PARAMETER Java8新增,表示该注解能写在类型变量的声明语句中
- TYPE_USE Java8新增,表示该注解能写在使用类型的任何语句中(例如声明语句、泛型和强制转换语句中的类型)
对于最后的:TYPE_PARAMETER 和 TYPE_USE 是Java8所新增的内容,咱们放到后续文章中详细讲解,延伸:Checker Framework。
@Inherited
标示注解是否可继承;这里的集成并不是说注解相互的继承,而是说父类注解能否被子类所感知。
public class Test {
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation1 {
}
@Retention(RetentionPolicy.RUNTIME)
@interface Annotation2 {
}
@Annotation1
static class Foo1 {
}
static class Foo2 extends Foo1 {
}
@Annotation2
static class Foo3 {
}
static class Foo4 extends Foo3 {
}
public static void main(String args[]) {
System.out.println("具有@Inherited:" + Arrays.toString(Foo2.class.getAnnotations()));
System.out.println("没有@Inherited:" + Arrays.toString(Foo4.class.getAnnotations()));
}
}
运行结果:
QIUJUERBOOK:main qiujuer$ java net.qiujuer.jumper.annotation.Test
具有@Inherited:[@net.qiujuer.jumper.annotation.Test$Annotation1()]
没有@Inherited:[]
@Repeatable
该元注解是Java8才引入的注解;用于标示一个注解是否可以重复使用。
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String value();
}
// 错误的案例
@Todo("main")
@Todo("show")
static class Foo {
}
在以前你是没法如此使用的,因为编译器不给予编译;那么我想要Todo多条信息怎么办??
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String[] value();
}
@Todo({"main", "show"})
static class Foo {
}
OK, 改成数组之后正常了,但是Java8允许更加特殊的方法来使用。
@Repeatable(TodoList.class)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@interface TodoList {
Todo[] value();
}
@Todo("main")
@Todo("show")
static class Foo {
}
上述案例此时是合法的,但是仅在Java8下能成功运行;其原理是通过一个具有Annotation Array的注解(TodoList)来接收数据。其获取的方式咱们也后续另起文章讲解。
好了,到此咱们的注解基础语法讲解完成了;注解是知道是什么了,但是注解如何使用呢?
注解的使用
一般而言注解常常会和反射一起使用;因为注解的信息是存储在AnnotatedElement中,AnnotatedElement是一个接口,实现其接口的有:Class、Constructor、Field、Method、Package。
在实际的使用中,我们会使用“java.lang.reflect”包下的一些反射API来获取上述类的实例,通过上述对象的实例我们可以使用AnnotatedElement提供的接口方法来访问标注的Annotaion信息。
- <T extends Annotation> T getAnnotation(Class<T> var1):该元素如果存在指定类型(var1)的注解,则返回这些注解;否则返回null。
- boolean isAnnotationPresent(Class<? extends Annotation> var1):如果该元素上存在该注解(var1)则返回True,反之返回False;内部实现是调用的第一个接口判断是否为null。
- Annotation[] getAnnotations():返回该元素上存在的所有的注解。包括父类的注解,前提是具有@Inherited元注解的存在。
- Annotation[] getDeclaredAnnotations():返回该元素上直接的注解,不包括父类的注解信息。这是与getAnnotations()最直接的区别。
- <T extends Annotation> T getDeclaredAnnotation(Class<T> var1):Java8新增,与第一个方法类似,仅仅限定在当前类上,不扫描父类。
- <T extends Annotation> T[] getAnnotationsByType(Class<T> var1):Java8新增,与@Repeatable配合使用,返回一个数组,包括父类的注解。
- <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> var1):Java8新增,与@Repeatable配合使用,返回一个数组,不包括父类的注解。
一个简单的例子:
public class Test {
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationSuper {
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationSuperWithInherited {
}
@Retention(RetentionPolicy.RUNTIME)
@interface AnnotationChild {
}
@AnnotationSuperWithInherited
@AnnotationSuper
static class FooSuper {
}
@AnnotationChild
static class FooChild extends FooSuper {
}
public static void main(String args[]) {
print(FooChild.class);
print(FooSuper.class);
}
private static void print(Class clx) {
System.out.println("============" + clx.getSimpleName() + "============");
System.out.println("所有:" + Arrays.toString(clx.getAnnotations()));
System.out.println("当前类上:" + Arrays.toString(clx.getDeclaredAnnotations()));
System.out.println("查询Child:" + clx.getAnnotation(AnnotationChild.class));
System.out.println("查询Super:" + clx.getAnnotation(AnnotationSuper.class));
System.out.println("查询WithInherited:" + clx.getAnnotation(AnnotationSuperWithInherited.class));
System.out.println("判断WithInherited:" + clx.isAnnotationPresent(AnnotationSuperWithInherited.class));
System.out.println("判断Super:" + clx.isAnnotationPresent(AnnotationSuper.class));
}
}
输出内容:
============FooChild============
所有:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationChild()]
当前类上:[@net.qiujuer.jumper.annotation.Test$AnnotationChild()]
查询Child:@net.qiujuer.jumper.annotation.Test$AnnotationChild()
查询Super:null
查询WithInherited:@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited()
判断WithInherited:true
判断Super:false
============FooSuper============
所有:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationSuper()]
当前类上:[@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited(), @net.qiujuer.jumper.annotation.Test$AnnotationSuper()]
查询Child:null
查询Super:@net.qiujuer.jumper.annotation.Test$AnnotationSuper()
查询WithInherited:@net.qiujuer.jumper.annotation.Test$AnnotationSuperWithInherited()
判断WithInherited:true
判断Super:true
Process finished with exit code 0
至此,Java注解的基础知识已全部完成,后续文章将讲解注解在运行时态的实战运用和编译时运用。
尾章
看累了吧?自己反复看一下,来一个案例试试~~
END!!!