[Java]自定义注解

【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权)
https://www.cnblogs.com/cnb-yuchen/p/17993690
出自【进步*于辰的博客

由于单独的一个或多个元注解无法进行测试,故本篇文章中的示例都是基于自定义注解。因此,大家在阅读代码时,可能会觉得有点云里雾里。无妨,疑惑是暂时的。
参考笔记一,P72.1、P76.1。

注: 本篇文章引入了两个知识点,会在举例时使用。

  1. 反射,详述可查阅博文《[Java]反射》;
  2. JavaDoc文档,推荐一篇博文《【Java学习笔记】【基础篇】07.JavaDoc以及两种使用方式》(转发)。

1、常见元注解

1.1 @target

源码:

@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();
}

作用:用于指定被注解的注解能注解于哪些 java 元素上(如:类、属性、方法等),类型为枚举ElementType[](关于枚举ElementType的具体取值,大家可自行查看源码)。在不指定具体ElementType时,表示被注解的注解可注解于任何 java 元素。此时,会根据被注解的注解所注解的类型自适应具体的ElementType

示例:

@Target({ElementType.TYPE,
        ElementType.FIELD})// --------A
@interface MyAnnotation {
}

@MyAnnotation// ----------------------B
class TestAnnotation {
    @MyAnnotation// ------------------D
    private Integer id;

    @MyAnnotation// ------------------D
    public void print() {
    }
}

A处指定ElementTypeTYPEFIELD,表示@MyAnnotation 可用于注解类和属性,因此,B、C 处编译通过,而 D 处编译报错。
若将 A 删除,则 B、C、D 三处都编译通过,因为@MyAnnotation@Target会根据@MyAnnotation所注解的类型自适应具体的ElementType

1.2 @Retention

源码:

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

作用:指定被注解的注解的生命周期,类型为枚举RetentionPolicy,有三个值可选:SOURCECLASSRUNTIME。当不指定RetentionPolicy时,默认为CLASS

值说明:

  1. SOURCE:表示被注解的注解只保留在 java 源文件中(后缀是.java的文件),JVM不会进行编译;
  2. CLASS:表示被注解的注解能被编译到 class 文件中(后缀是.class的文件,即 class字节码文件),但JVM会忽略,即无法获取;
  3. RUNTIME:表示被注解的注解能被编译到JVM中,可通过反射获取,实际开发中的自定义注解的生命周期几乎都是这个。

1.3 @Documented

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}

作用:指定被注解的注解能跟随 java 文件到JavaDoc文档中。

1.4 @Inherited

源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}

作用:指定被注解的注解所注解的类的子类是否可继承此被注解的注解。

示例。
情形一:(没有定义@Inherited

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
}

@MyAnnotation
class Person {
}

class Teacher extends Person {
}

public class TestAnnotation {
    public static void main(String[] args) throws Exception {
        Class personClass = Class.forName("com.neusoft.boot.Person");// 使用反射,通过全限定名获取Class对象
        Class teacherClass = Class.forName("com.neusoft.boot.Teacher");

        MyAnnotation a1 = (MyAnnotation) personClass.getAnnotation(MyAnnotation.class);// 获取注解在类Person上的@MyAnnotation
        sout a1;// 打印:@com.neusolt.boot.MyAnnotation()
        MyAnnotation a2 = (MyAnnotation) teacherClass.getAnnotation(MyAnnotation.class);// 获取注解在类Teacher上的@MyAnnotation
        sout a2;// 打印:null
    }
}

情形二:

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
}

@MyAnnotation
class Person {
}

class Teacher extends Person {
}

public class TestAnnotation {
    public static void main(String[] args) throws Exception {
        Class personClass = Class.forName("com.neusoft.boot.Person");
        Class teacherClass = Class.forName("com.neusoft.boot.Teacher");

        MyAnnotation a1 = (MyAnnotation) personClass.getAnnotation(MyAnnotation.class);
        sout a1;// 打印:@com.neusolt.boot.MyAnnotation()
        MyAnnotation a2 = (MyAnnotation) teacherClass.getAnnotation(MyAnnotation.class);
        sout a2;// 打印:@com.neusolt.boot.MyAnnotation()
    }
}

2、自定义注解

上文中常见元注解的示例都是基于自定义注解,相信大家对自定义注解已有了初步了解。

2.1 概述

所有注解都自动继承java.lang.annotation.Annotation接口,注解由@interface声明,上述源码中的value()是注解元素,类似于成员属性。
注解元素必须由()结尾,可以使用default定义默认值,使用注解时必须为所有无默认值的注解元素赋值。

举个例:

@Target(ElementType.TYPE)
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface MyAnnotation {
    String name() default "cs";
}

@MyAnnotation(name = "csdn")// 这里的 (name = "csdn") 可省略
class Person {
}

注解@MyAnnotation有一个注解元素name,默认值为"cs"。由于name有默认值,故使用此注解时不必须为name赋值。

2.2 注意事项

  1. 注解元素必须由public修饰,默认是public
  2. 一般注解元素以名词命名,若只有一个,建议名称为value
  3. 注解元素类型只能是基本数据类型、基本数据类型数组或注解类型(注解嵌套);
  4. default指定注解元素默认值时,值类型必须与注解元素类型相同。

2.3 使用细节

  1. 若无注解元素,可省略()(小括号);
  2. 若注解元素类型为数组,且赋值时只有一个值时,可以省略{}(花括号);
  3. 如果只有一个注解元素,且注解元素名为value。无论其是什么类型,都可省略前缀。如上述例子:需要name = "csdn"为注解元素name赋值,如果注解元素名为value,则可省略“value =”;
  4. ElementTypePACKAGE,则此注解用于注解在package-info.java文件中(PS:这个文件默认是不创建的。在idea中,双击shift可搜索到,打开时才会创建。其用途尚未可知,欢迎大家留言!),而不是类文件(xx.java)的第一行的package...上。

4、最后

本文中的示例是为了方便大家理解自定义注解的定义和使用方法而简单举出的,不一定有实用性。大家在看完本篇文章后,可能仍有疑惑,不妨自行测试一下,就都理解了。

本文完结。

热门相关:史上第一宠婚:慕少的娇妻   飞行中的艳遇   琴弦上的爱   女员工:猥亵交易   牡丹令