编译时注解项目实践之注解

前言

Extra 是最近为了实践编译时注解写的一个项目,主要功能是可以直接通过注解获取 Activity 通过 Intent 传递的数据。主要涉及到注解处理及自定义 gradle 插件相关的知识,下面会围绕这些知识总结一些心得

注解

当前有很多流行的库都是用了注解这种语法,如ButterKnife、Dagger2、Retrefit等,熟练的使用注解,不仅可以让代码看起来简洁明了,还可以在动态运行时执行你想要的操作,甚至还能帮我生成代码。

注解类型

注解类型主要分为三种,系统注解、元注解、自定义注解。

系统注解

1、@Override

1
2
3
4
5
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

用于告知编译器,我们需要覆写超类的当前方法。如果某个方法带有该注解但并没有覆写超类相应的方法,则编译器会生成一条错误信息。 可适用元素为方法,仅仅保留在java源文件中|

2、@Deprecated

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}

用于告知编译器,某一程序元素(比如方法,成员变量)不建议使用时,应该使用这个注解。Java在javadoc中推荐使用该注解,一般应该提供为什么该方法不推荐使用以及相应替代方法。可适合用于除注解类型声明之外的所有元素,保留时长为运行时VM。

3、@SuppressWarnings

1
2
3
4
5
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}

用于告知编译器忽略特定的警告信息,例在泛型中使用原生数据类型。 @SuppressWarnings可适合用于除注解类型声明和包名之外的所有元素,仅仅保留在java源文件中。

此注解还可以接收参数,参数介绍如下

参数 含义
deprecation 使用了过时的类或方法时的警告
unchecked 执行了未检查的转换时的警告
fallthrough 当Switch程序块进入进入下一个case而没有Break时的警告
path 在类路径、源文件路径等有不存在路径时的警告
serial 当可序列化的类缺少serialVersionUID定义时的警告
finally 任意finally子句不能正常完成时的警告
all 以上所有情况的警告

元注解

1、@Documented

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

表示拥有该注解的元素可通过javadoc此类的工具进行文档化。该类型应用于注解那些影响客户使用带注释(comment)的元素声明的类型。如果类型声明是用Documented来注解的,这种类型的注解被作为被标注的程序成员的公共API。

2、@Inherited

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

表示该注解类型被自动继承,如果用户在当前类中查询这个元注解类型并且当前类的声明中不包含这个元注解类型,那么也将自动查询当前类的父类是否存在Inherited元注解,这个动作将被重复执行知道这个标注类型被找到,或者是查询到顶层的父类。

3、@Retention

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}

表示该注解类型的注解保留的时长。当注解类型声明中没有@Retention元注解,则默认保留策略为RetentionPolicy.CLASS。关于保留策略(RetentionPolicy)是枚举类型,共定义3种保留策略,如下表:

RetentionPolicy 含义
SOURCE 仅存在Java源文件,经过编译器后便丢弃相应的注解
CLASS 存在Java源文件,以及经编译器后生成的Class字节码文件,但在运行时VM不再保留注释
RUNTIME 存在源文件、编译生成的Class字节码文件,以及保留在运行时VM中,可通过反射性地读取注解

4、@Target

1
2
3
4
5
6
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}

表示该注解类型的所使用的程序元素类型。当注解类型声明中没有@Target元注解,则默认为可适用所有的程序元素。如果存在指定的@Target元注解,则编译器强制实施相应的使用限制。关于程序元素(ElementType)是枚举类型,共定义8种程序元素,如下表:

ElementType 含义
ANNOTATION_TYPE 注解类型声明
CONSTRUCTOR 构造方法声明
FIELD 字段声明(包括枚举常量)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注解类型)或枚举声明

自定义注解

下面以 Extra 项目为例,实战下自定义注解的定义

定义一个注解,首先我们要明确三个问题

  • 生命周期是什么?
  • 针对的目标是什么类型?
  • 内部是否有参数?

针对 ExtraParam 我们可以如下作答:
生命周期:编译时,因为要生成自动获取Intent传递数据的代码
针对的目标:变量
内部是否有参数:有,因为有时候我们希望变量的名字跟传递数据时Bundle的Key不同

所以 ExtraParam 定义就出来了

1
2
3
4
5
6
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface ExtraParam {
//key in bundle
String value() default "";
}

下篇文章讲注解解析器(APT),敬请期待

参考

Java注解(Annotation)
Java 技术之注解 Annotation