【Java基础】注解

149 阅读3分钟

元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为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