在很多程序设计语言里,依赖注入是一种比价流行的设计模式,在Android开发中很多框架也是采用的是依赖注入的方式,比如说我们常用框架ButterKnife和Dagger2。我们只是知道如何使用,但是有多少程序猿了解其原理,这些框架都是采用编译时注解。
从JDK 5开始,Java增加了注解,注解是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
注解分为标准注解和元注解。
标准注解有4中:
- @Override:对覆盖父类中的方法进行标记,如果被标记的方法并没有实际重写父类中的方法,则编译器会发出错误警告。
- @Deprecated:对已过世的方法添加注解,当程序猿使用这些方法时,将会在编译时显示提示信息。
- SuppressWarning:选择性的忽略代码中的警告,通常使用方式有以下忽略方式:
deprecation -- 使用了不赞成使用的类或方法时的警告
unchecked -- 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。
fallthrough -- 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。
path -- 在类路径、源文件路径等中有不存在的路径时的警告。
serial -- 当在可序列化的类上缺少 serialVersionUID 定义时的警告。
finally -- 任何 finally 子句不能正常完成时的警告。
all -- 关于以上所有情况的警告。
- SafeVarargs:JDK 7新增,用来声明使用了可变长度参数的方法,其在与泛型类一起使用时不会出现类型安全问题。
除了标注注解,还有元注解,它用来注解其他注解,从而创建新的注解。元注解有以下几种:
- @Targe:表示所修饰的对象使用范围。
- @Inherited:表示注解可以被继承;但并不是注解本身可以继承,而是说一个父类被@Inherited注解过,它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解。
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
//Class B 也含有注解@Test
public class B extends A {}
- @Documented:表示这个注解应该被JavaDoc工具记录。
- @Retention:用来声明注解的保留策略。
- @Repeatable:JDK 8新增,允许一个注解在同一个声明类型(类、属性或方法)上多次使用。
@interface Users {
User[] value();
}
@Repeatable(Users.class)
@interface User{
String name default "";
}
@Person(name="artist")
@Person(name="coder")
@Person(name="PM")
public class SuperUser{
}
- @Repeatable注解了User,而括号里的类相当于一个容器注解Users(用来存放其它注解的地方,其本身是一个注解).注解Users属性是一个数组。
在我们自己自定义注解中,用到最多是@Targe和@Retention注解,我们重点讲解。
@Targe的参数是ElementType类型的值,其源码如下:
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
//能修饰类、接口或枚举类型
TYPE,
/** Field declaration (includes enum constants) */
//能修饰成员变量或枚举中的常量
FIELD,
/** Method declaration */
//能修饰方法
METHOD,
/** Formal parameter declaration */
//能修饰参数
PARAMETER,
/** Constructor declaration */
//能修饰构造方法
CONSTRUCTOR,
/** Local variable declaration */
//能修饰局部变量
LOCAL_VARIABLE,
/** Annotation type declaration */
//能修饰注解
ANNOTATION_TYPE,
/** Package declaration */
//能修饰包
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
//类型参数声明
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
//使用类型
TYPE_USE
}
- 对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。
@Retention注解有三种类型,分别表示不同级别的保留策略。保留策略可以理解为生命周期,表示注释信息存留时间的长短。其参数是RetentionPolicy枚举值。其RetentionPolicy的源码如下:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
- RetentionPolicy.SOURCE:源码级注解。注解信息只会保留在.java源码中,源码在编译后,注解信息被丢失,不会保留在.class中。
- RetentionPolicy.CLASS:编译时注解。注解信息会保留在.java源码以及.class中。当运行Java程序时,JVM会丢弃该注解信息,不会保留在JVM中。
- RetentionPolicy.RUNTIME:运行时注解。当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息。
每1个Annotation对象,都会有唯一的RetentionPolicy属性。以上的注解的生命周期长短时间:SOURCE < CLASS < RUNTIME。
下面通过自定义一个防重复点击的注解,clickIntervals为防重复点击的时间。
- 基本定义
自定义一个注解只需要使用@interface关键字,实例如下:
public @interface SingleClick {
}
- 定义成员变量
注解只有成员变量,没有方法。注解的成员变量在注解定义中以“无形参的方法”形式来声明,其“方法名”定义了该成员变量的名字,其返回值定义了该成员变量的类型。
public @interface SingleClick {
/**
* 设置防重复点击时间
* @return 防重复点击时间,若没有设置默认为1s
*/
long clickIntervals() default 1000;
}
使用关键字default设置默认值
- 定义注解的使用范围
通过@Targe来声明其使用的范围,比如下面使用在方法上面,使用ElementType.METHOD进行声明;如果使用在类或接口上面,则采用ElementType.TYPE。根据上面ElementType枚举值的含义,选择自己需要的枚举值。
@Target(ElementType.METHOD)
public @interface SingleClick {
/**
* 设置防重复点击时间
* @return 防重复点击时间,若没有设置默认为1s
*/
long clickIntervals() default 1000;
}
- 定义注解的保留策略
实际是设置注解的生命周期,根据自己场景的需要,适当的选择其保留策略。因为防重复点击是程序运行时的操作,则应该定义运行时注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
/**
* 设置防重复点击时间
* @return 防重复点击时间,若没有设置默认为1s
*/
long clickIntervals() default 1000;
}
- 上面可以说已经定义了一个有效的注解,看一下使用方式:
@SingleClick(clickIntervals = 3000)
public void firstOnClick(View view) {
Log.e(TAG, "firstOnClick: ");
}
- 因为注解SingleClick我声明使用范围方法上,所以只能在方法上使用,clickIntervals = 3000 代表3s内防重复点击。
注意只是靠一个注解,是不能完成防重复点击,需要结合AOP变成才能真正的防重复点击的功能。
注解SingleClick是一个运行时注解,我们也可以采用源码级注解进行一些检查性工作,比如 @Override 和 @SuppressWarnings 都是采用RetentionPolicy.SOURCE,其源码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is <i>not</i> an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* <p> The string {@code "unchecked"} is used to suppress
* unchecked warnings. Compiler vendors should document the
* additional warning names they support in conjunction with this
* annotation type. They are encouraged to cooperate to ensure
* that the same names work across multiple compilers.
* @return the set of warnings to be suppressed
*/
String[] value();
}