Java 文档 - 元注解 meta-annotation

373 阅读6分钟

在JDK中提供了几个标准的用来对注解类型进行注解的注解类(annotation of annotation),我们称之为 meta-annotation(元注解),他们分别是:

  • Documented:标志将此注解包含至javadoc中
  • Inherited: 注解允许继承
  • Retention: 定义注解保存级别
  • Target: 定义注解适用的目标
  • Repeatable: 可以重复的注解 jdk 1.8新增
  • Native:表示一个定义常量值的字段可以从本机代码被引用。

我们可以使用元注解来对我们自定义的注解类型进行注解

@Target注解

Target注解的作用是:描述注解的使用范围(即被修饰的注解可以用在什么地方).

Target注解用来说明那些被它所注解的注解类可修饰的对象范围:注解可以用于修饰 packages、types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数),在定义注解类时使用了@Target 能够更加清晰的知道它能够被用来修饰哪些对象,它的取值范围定义在ElementType 枚举中.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    /**
     * Returns an array of the kinds of elements an annotation type
     * can be applied to.
     * @return an array of the kinds of elements an annotation type
     * can be applied to
     */
    ElementType[] value();
}

@Retention注解

Reteniton注解的作用是:描述注解保留的时间范围(即:被描述的注解在它所修饰的类中可以被保留到何时).

Reteniton注解用来限定那些被它所注解的注解类在注解到其他类上以后,可被保留到何时,一共有三种策略,定义在RetentionPolicy枚举中.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
    RetentionPolicy value();
}

@Documented注解

Documented注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。

/**
 * Indicates that annotations with a type are to be documented by javadoc
 * and similar tools by default.  This type should be used to annotate the
 * declarations of types whose annotations affect the use of annotated
 * elements by their clients.  If a type declaration is annotated with
 * Documented, its annotations become part of the public API
 * of the annotated elements.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

这里验证@Documented的作用,我们创建一个自定义注解:

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyDocumentedt {

    public String value() default "这是@Documented注解为文档添加的注释";

}

然后创建一个测试类,在方法和类上都加入自定义的注解

@MyDocumentedt
public class MyDocumentedTest {
    /**
     * 测试 document
     * @return String the response
     */
    @MyDocumentedt
    public String test(){
        return "sdfadsf";
    }
}

打开java文件所在的目录下,打开命令行输入:

javac .\MyDocumentedt.java .\MyDocumentedTest.java

javadoc -d doc .\MyDocumentedTest.java .\MyDocumentedt.java

打开生成的doc文件夹,打开index.html,可以发现在类和方法上都保留了 MyDocumentedt 注解信息。

@Inherited注解

Inherited注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解)。

/**
 * Indicates that an annotation type is automatically inherited.  If
 * an Inherited meta-annotation is present on an annotation type
 * declaration, and the user queries the annotation type on a class
 * declaration, and the class declaration has no annotation for this type,
 * then the class's superclass will automatically be queried for the
 * annotation type.  This process will be repeated until an annotation for this
 * type is found, or the top of the class hierarchy (Object)
 * is reached.  If no superclass has an annotation for this type, then
 * the query will indicate that the class in question has no such annotation.
 *
 * <p>Note that this meta-annotation type has no effect if the annotated
 * type is used to annotate anything other than a class.  Note also
 * that this meta-annotation only causes annotations to be inherited
 * from superclasses; annotations on implemented interfaces have no
 * effect.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

通过代码来进行验证,创建一个自定义注解

@Target({ElementType.TYPE})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface MyInherited {
}

验证

@MyInherited
public class A {
    public static void main(String[] args) {
        System.out.println(A.class.getAnnotation(MyInherited.class));
        System.out.println(B.class.getAnnotation(MyInherited.class));
        System.out.println(C.class.getAnnotation(MyInherited.class));
    }
}

class B extends A{
}

class C extends B{
}

执行main方法,从控制台中可以看到打印的信息

@Repeatable注解

重复注解:即允许在同一申明类型(类,属性,或方法)前多次使用同一个类型注解。

/**
 * The annotation type {@code java.lang.annotation.Repeatable} is
 * used to indicate that the annotation type whose declaration it
 * (meta-)annotates is <em>repeatable</em>. The value of
 * {@code @Repeatable} indicates the <em>containing annotation
 * type</em> for the repeatable annotation type.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
}

在java8 以前,同一个程序元素前最多只能有一个相同类型的注解;如果需要在同一个元素前使用多个相同类型的注解,则必须使用注解“容器”。 java8之前的做法

public @interface Roles {
    Role[] roles();
}

public @interface Roles {
    Role[] value();
}

public class RoleAnnoTest {
    @Roles(roles = {@Role(roleName = "role1"), @Role(roleName = "role2")})
    public String doString(){
        return "";
    }
}

java8之后增加了重复注解,使用方式如下:

public @interface Roles {
    Role[] value();
}

@Repeatable(Roles.class)
public @interface Role {
    String roleName();
}

public class RoleAnnoTest {
    @Role(roleName = "role1")
    @Role(roleName = "role2")
    public String doString(){
        return "";
    }
}

不同的地方是,创建重复注解 Role 时,加上@Repeatable,指向存储注解 Roles,在使用时候,直接可以重复使用 Role 注解。从上面例子看出,java 8里面做法更适合常规的思维,可读性强一点。但是,仍然需要定义容器注解。

两种方法获得的效果相同。重复注解只是一种简化写法,这种简化写法是一种假象:多个重复注解其实会被作为“容器”注解的 value 成员的数组元素处理。

@Native注解

/**
 * Indicates that a field defining a constant value may be referenced
 * from native code.
 *
 * The annotation may be used as a hint by tools that generate native
 * header files to determine whether a header file is required, and
 * if so, what declarations it should contain.
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface Native {
}

类型注解

Java8 为 ElementType 枚举增加了TYPE_PARAMETER、TYPE_USE两个枚举值,从而可以使用 @Target(ElementType_TYPE_USE) 修饰注解定义,这种注解被称为类型注解,可以用在任何使用到类型的地方。

在 java8 以前,注解只能用在各种程序元素(定义类、定义接口、定义方法、定义成员变量…)上。从 java8 开始,类型注解可以用在任何使用到类型的地方。

TYPE_PARAMETER:表示该注解能写在类型参数的声明语句中。

TYPE_USE:表示注解可以再任何用到类型的地方使用,比如允许在如下位置使用:

  • 创建对象(用 new 关键字创建)

  • 类型转换

  • 使用 implements 实现接口

  • 使用 throws 声明抛出异常

    public class TypeUserTest {

    public static void main(String[] args) {
        String str = "str";
        Object obj = (@isNotNull Object) str;
    }
    

    }

    @Target(ElementType.TYPE_USE) @interface isNotNull{ }

这种无处不在的注解,可以让编译器执行更严格的代码检查,从而提高程序的健壮性。

作者:爱西考的王同学
链接:juejin.cn/post/684490…
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。