Java基础,注解的正确使用以及JDK1.8带来的改变

224 阅读4分钟

注解在我们编码的过程中出现的概率可谓是极度的高,但却是我们容易不重视的点,很多优秀框架的实现都需要借助注解的能力来实现,所以我们必须要掌握它,下面我们直接通过一个样例进入正题。

自定义注解样例:

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface TestAnnotation {
    int value() default 1;
    String name() default "朱凯_Dev";
}

上面样例就是注解的定义方式,使用关键字@interface标记一个类,然后标注@Target、@Retention两个元注解,这就是注解自定义的普遍方式。

注解在本质上分为两类,一种是元注解,一种是业务注解(非官方定义)。

  • 元注解:注解的注解。
  • 业务注解:元注解标记创建的非元注解注解。 元注解只能标注在注解之上,创建元注解只需要@Target注解中指定ElementType.ANNOTATION_TYP。在进行自定义注解时,我们使用关键字@interface标注一个类,再使用元注解标记,关键字@interface只是一个标记,接下来我们将重点说明四个元注解。

JDK自带四个元注解:

  1. @Target  2. @Retention  3. @Document  4. @Inherited

@Target

@Target用来指定定义注解的目标对象,@Target的值必须是ElementType枚举类,我们看一下有那些枚举常量。

  • TYPE:类、接口(包括注解类型)或枚举声明
  • FIELD:字段属性(包括枚举常量)
  • METHOD:方法之上
  • PARAMETER:方法参数
  • CONSTRUCTOR:构造函数
  • LOCAL_VARIABLE:局部变量
  • ANNOTATION_TYPE:泛型类型(标记后为元注解)
  • PACKAGE:包声明(如:package xxxx.xxxx.xxxx;)
  • TYPE_PARAMETER:类型参数(jdk1.8)
  • TYPE_USE:类型使用(jdk1.8)

当你定义的泛型希望在哪里使用时,你就指定特定的ElementType枚举常量,注意:@Target可以指定多个

@Retention

@Retention用来指定定义的注解保留情况,总共有三种情况。

  • SOURCE:源码阶段
  • CLASS:.class文件阶段
  • RUNTIME:运行时阶段

三种保留情况应该都是理解的,但是针对这三种情况分别对应的应用场景是什么呢?我给大家列一个表格进行举例,方便大家理会具体的使用场景。

@Retention值场景
SOURCEAPT技术、IDE语法检查(比如:常用的@StringRes、DrawableRes注解等)
CLASSASM插桩技术、AspectJ(AOP编程)
RUNTIME反射

在泛型定义时,可以根据自己实际的业务场景指定对应的保留情况。

@Document

拥有这个注解的元素可以被javadoc此类的工具文档化。它代表着此注解会被javadoc工具提取成文档。在doc文档中的内容会因为此注解的信息内容不同而不同。相当于@return,@param 等。

@Inherited

定义注解类A,A中使用@Inherited,在B中使用注解A,C继承B,C可以继承B中使用的注解A。

JDK1.8的注解加强

jdk1.8新增了重复注解与类型注解。

重复注解

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

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    int value() default 1;
    String name() default "朱凯_Dev";
}

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotations {
    MyAnnotation[] value() ;
}

public class TestAnnotation {
    @MyAnnotations(value = {@MyAnnotation(value = 2), @MyAnnotation(value = 3)})
    public void showAnnotation() {

    }
}

按上面步骤,需要先定义一个注解类,再定义容器注解类,属性value的类型必须是前面定义的注解类,然后通过容器注解类指定相同注解。注意:容器注解的属性必须是value,切勿指定values之类的字段。

jdk1.8新增的元注解 @Repeatable

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

其使用方式:

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    int value() default 1;
    String name() default "朱凯_Dev";
}

@Repeatable(MyAnnotations.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    int value() default 1;

    String name() default "朱凯_Dev";
}

public class TestAnnotation {
    @MyAnnotation(value = 1)
    @MyAnnotation(value = 2)
    public void showAnnotation() {

    }
}

与原来的使用多个相同注解相比,都是需要定义容器注解;区别在当注解上使用@Repeatable标注,并指定容器注解类型之后,使用上便可以直接使用注解类,而不在需要使用容器注解来指定,这在使用上更符合常规思维,可读性也更强。

类型注解

jdk1.8在@Target的ElementType中新加入了二个常量。

  • TYPE_PARAMETER:类型参数
  • TYPE_USE:类型使用

TYPE_PARAMETER

类型参数可能不太好理解,举例子来说明吧!

@Target({ElementType.TYPE_PARAMETER})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    int value() default 1;
    String name() default "朱凯_Dev";
}

public class TestAnnotation {
    public <@MyAnnotation T>void showAnnotation( T l) {

    }
}

这里的泛型就是一个泛型参数。

TYPE_USE

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

@Target({ElementType.TYPE_USE})
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    int value() default 1;
    String name() default "朱凯_Dev";
}

@MyAnnotation
public class TestAnnotation {

    private @MyAnnotation int num;
    private @MyAnnotation String msg;

    public <@MyAnnotation T> void showAnnotation(@MyAnnotation String l) throws @MyAnnotation ClassNotFoundException{

    }
}