什么是注解 Java 中的注解类型
(Annotation Types,下称注解)可以理解成是对代码的一种特殊标记,我们可以把注解标记在类、方法、变量、参数等 Java 元素上。这个标记可以给编写代码的人看,但是它主要还是提供给程序本身进行解析。
也就是说,注解本身其实不能起到作用,还需要其他程序去主动解析注解来采取相应的动作。如果没有相应的解析代码,那么注解就起不到任何作用。
如何定义一个注解 注解 注解其实就是一种新的特殊的接口类型,为了和普通的接口区分开,在定义注解时需要在关键字 interface
前添加 @
符号,例如:
1 2 public @interface MyAnnotation {}
同时,我们定义的每一个注解都会自动实现 java.lang.annotation.Annotation
接口。
注解元素 我们还可以在注解中添加一些 注解类型元素
(Annotation Type Elements,下称注解元素)。只需要在注解的实现部分添加一些方法声明 (method declarations),这些方法就会被定义成注解元素。
但是这些方法声明中不能 包含形式参数、类型参数、抛出异常 (throws clause),方法也不能 被 defalut、static、private、protected 等修饰,只能 使用 public (不写则默认为 public)。
另外,这些声明的方法的返回值类型,只能是以下几种:
原始数据类型
字符串
类
枚举类型
注解类型
以上类型的一维数组
例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 enum Level {BAD, INDIFFERENT, GOOD}class User {} public @interface MyAnnotation { int age () ; String name () ; String[] names(); Class<User> userClass () ; Level level () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class User {} public @interface MyAnnotation { Integer age () ; User user () ; String[][] names(); int number (int id) ; String name () throws RuntimeException; static int height () ; default int id () ; }
我们还可以为注解元素指定默认值,例如:
1 2 3 public @interface MyAnnotation { int age () default 20 ; }
注解的分类 根据注解中的注解元素个数,还可以将注解分为:
标记注解:注解中不含有注解元素
单元素注解:注解中只有一个注解元素
普通注解:注解中含有超过一个注解元素
其中,我们约定单元素注解中的元素统一命名为 value
,如:
1 2 3 public @interface MyAnnotation { int value () ; }
常用的注解 元注解 元注解就是用来修饰注解的注解,经常用到的有:
Target
Retention
Inherited
Document
@Target 这个元注解被用来说明某个注解可以用来修饰什么 Java 元素,声明如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
它的取值范围被定义在枚举类 ElementType
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE }
在 @Target 注解本身就使用了 @Target(ElementType.ANNOTATION_TYPE)
,说明 @Target 注解只能用来修饰注解。
@Retention 这个注解用来说明某个注解的存活时间。
1 2 3 4 5 6 7 8 9 10 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value () ; }
它的取值范围被定义在枚举类 RetentionPolicy
中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public enum RetentionPolicy { SOURCE, CLASS, RUNTIME }
@Inherited 这个注解用来表示某个注解在类继承的时候,父类上的注解是否可以被子类继承到。
这个注解只是用来标记是否能被继承,因此没有任何注解元素,是一个标记注解:
1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited {}
其他注解 @Override 该注解用来标记一个方法在重写父类(或接口)中的方法,也是一个标记注解。当编译器遇到这个注解时,会主动检查重写的条件,防止出现一些低级错误,如参数列表不符合、抛出了更宽泛的异常等。因此这个注解的存活周期只到编译时 (RetentionPolicy.SOURCE):
1 2 3 4 @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override {}
@Deprecated 这个注解用来标记某个类、方法、成员变量等是否被弃用。编译器会主动发出警告,如果一个弃用的 Java 元素被使用或者被重写在非 Deprecated 的方法中 (Compilers warn when a deprecated program element is used or overridden in non-deprecated code.)。
1 2 3 4 5 @Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated {}
注解的使用 普通注解 在使用普通注解时需要指定注解中定义的所有注解元素的取值 (含有默认值的注解元素可以不指定,会使用默认值)。同时不能指定注解中没有定义的注解元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name () ; int age () default 20 ; String location () ; } public class Main { @MyAnnotation(name="xuewen", age = 21, location = "China") private User userA; @MyAnnotation(name="xuewen", location = "China") private User userB; @MyAnnotation(name="xuewen") private User userC; @MyAnnotation(name="xuewen", location = "China", level="LEVEL_A") private User userD; }
标记注解 标记注解可以被直接使用,不需要给注解元素赋值。
1 2 3 4 5 6 7 8 9 10 11 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation {} public class Main { @MyAnnotation private User user; }
注意,如果一个注解中的所有注解元素都具有默认值 ,那么该注解也可以被当作标记注解使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name () default "xuewen" ; int age () default 20 ; String location () default "China" ; } public class Main { @MyAnnotation private User user; }
单元素注解 单元素注解只有一个特殊的地方,即在使用时可以不指定注解元素的名称(但是在定义注解时要按照约定来定义注解元素)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value () ; } public class Main { @MyAnnotation(value = "Value") private User userA; @MyAnnotation("Value") private User userB; }
以下使用方式是错误的,因为在定义单元素注解时没有按照约定将注解元素名称定义为 value
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String name () ; } public class Main { @MyAnnotation(value = "Value") private User userA; @MyAnnotation("Value") private User userB; }
注意,如果一个注解中除了 value 注解元素之外还有其他注解,并且其他注解都含有默认值,那么这个注解也可以被当做单元素注解使用,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value () ; String name () default "xuewen" ; int age () default 20 ; } public class Main { @MyAnnotation(value = "Value") private User userA; @MyAnnotation("Value") private User userB; @MyAnnotation(value = "Value", name = "xuewnG") private User userC; }
参考资料:
The Java Language Specification, Java SE 8 Edition - Chapter 9.6. Annotation Types JDK 1.8.0_231 Source Code