注解在我们编码的过程中出现的概率可谓是极度的高,但却是我们容易不重视的点,很多优秀框架的实现都需要借助注解的能力来实现,所以我们必须要掌握它,下面我们直接通过一个样例进入正题。
自定义注解样例:
@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自带四个元注解:
- @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值 | 场景 |
|---|---|
| SOURCE | APT技术、IDE语法检查(比如:常用的@StringRes、DrawableRes注解等) |
| CLASS | ASM插桩技术、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{
}
}