元注解
在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为meta-annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个:
另外还有@Documented与@Inherited元注解,前者用于被 javadoc工具提取成文档,后者表示允许子类继承父类中定义的注解。
@Target
注解标记另一个注解,以限制可以应用注解的Java元素类型。目标注解指定以下元素类型之一作为其值:
- ElementType.ANNOTATION_TYPE 可以应用于注解类型
- ElementType.CONSTRUCTOR 可以应用于构造函数
- ElementType.FIELD 可以应用于字段或属性
- ElementType.LOCAL_VARIABLE 可以应用于局部变量
- ElementType.METHOD 可以应用于方法级注解
- ElementType.PACKAGE 可以应用于包声明
- ElementType.PARAMETER 可以应用于方法的参数
- ElementType.TYPE 可以应用于类的任何元素
@Retention
注解指定标记注解的存储方式:
- RetentionPolicy.SOURCE 标记的注解仅保留在源级别中,并被编译器忽略
- RetentionPolicy.CLASS 标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略
- RetentionPolicy.RUNTIME 标记的注解由JVM保留,因此运行时环境可以使用它
@Rentention 三个值中SOURCE < CLASS < RUNTIME, 即CLASS包含了SOURCE,RUNTIME包含了CLASS。
举个例子:
// @Target(ElementType.TYPE) // 只能在类上标记该注解
@Target({ElementType.TYPE, ElementType.FIELD}) // 允许在类与类属性上标记该注解
@Retention(RetentionPolicy.SOURCE) // 注解保留在源码中
public @interface Grape {
}
注解类型元素
在上文元注解中,允许在使用注解时传递参数。我们也能让自定义注解的主题包含annotation type element(注解类型元素)声明,它们看起来很像方法,可以定义可选的默认值。
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Grape {
String value(); // 无默认值
int age() default 1; // 有默认值
}
注意:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。
@Grape(value = "甜", age = 2)
int i;
@Grape("酸")
int j;
注解应用场景
按照@Retention元注解定义的注解存储方式,注解可以在三种场景下使用:
SOURCE
RetentionPolicy.SOURCE,作用于源码级别的注解,可提供给IDE语法检查、APT等场景使用。
import org.junit.Test;
@Grape
public class AnnotationUnitTest {
@Test
public void test() {
}
}
在类中使用SOURCE级别的注解,其在编译之后的class中会被丢弃。
import org.junit.Test;
public class AnnotationUnitTest {
public AnnotationUnitTest() {
}
@Test
public void test() {
}
}
IDE语法检查
在 Android开发中,support-annotations与androidx.annotation中均有提供@IntDef注解,此注解的定义如下:
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.ANNOTATION_TYPE)
public @interface IntDef {
int[] value() default {};
boolean flag() default false;
boolean open() default false;
}
Java中Enum(枚举)的实质是特殊单例的静态成员变量,在运行期所有枚举类作为单例,全部加载到内存中。比常量多5到10倍的内存占用。
此注解的意义在于能够取代枚举,实现如方法入参限制。
APT注解处理器
APT全称为:"Anotation Processor Tools",意为注解处理器。顾名思义,其用于处理注解。编写好的Java源文件,需要经过 javac的编译,翻译为虚拟机能够加载解析的字节码Class文件。注解处理器是javac自带的一个工具,用来在编译时期扫描处理注解信息。你可以为某些注解注册自己的注解处理器。 注册的注解处理器由javac调起,并将注解信息传递给注解处理器进行处理。
注解处理器是对注解应用最为广泛的场景。在Glide、EventBus3、Butterknifer、Tinker、ARouter等等常用框架中都有注解处理器的身影。但是你可能会发现,这些框架中对注解的定义并不是 SOURCE 级别,更多的是CLASS级别,别忘了:CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。
关于注解处理器的实现,在后续课程中会有相当多的介绍。此处先不进行详细介绍。
CLASS