注解的语法

注解是一种接口,但使用 @interface 修饰符修饰。所有的注解(接口)都隐性的继承自 java.lang.annotation.Annotation 接口,

注解内的元素类型可以是以下任何一种:

  • 基本数据类型(byte,short,int,long,char,double,float,boolean)
  • String
  • Class
  • 枚举
  • 注解
  • 数组

下面代码的声明都是合法的:

  1. public @interface BugReport {
  2. enum Status {UNCONFIRMED, CONFIRMED, FIXED, NOTABUG}; // an enum
  3. boolean showStopper() default false;
  4. String assignedTo() default "none";
  5. Class<?> testCase() default Void.class;
  6. Status status() default Status.UNCONFIRMED;
  7. Reference ref() default @Reference(); // an annotation type
  8. String[] reportedBy();
  9. }

标准注解

java.longjava.lang.annotationjavax.annotation 包中定义了一些标准注解,其中 5 个是元注解。

注解 适用范围
普通
注解
Deprecated All
SafeVarargs 除了包和注解都适用
Override 方法
FunctionalInterface 接口
PostConstruct 方法
PreDestory 方法
Resource 类、接口、方法、属性
Resources 类、接口
Generated All
元注解 Target 注解
Retention 注解
Documented 注解
Inherited 注解
Repeatable 注解

元注解

@Target

@Target 注解用于限定注解的适用范围,需要传入一个 ElementType 枚举类的值。

Element Type Annotation Applies To
1 ANNOTATION_TYPE Annotation type declarations
2 PACKAGE Packages
3 TYPE Classes (including enum) and interfaces (including annotation types)
4 METHOD Methods
5 CONSTRUCTOR Constructors
6 FIELD Fileds (including enum constants)
7 PARAMETER Method or constructor parameters
8 LOCAL_VARIABLE Local variables
9 TYPE_PARAMETER Type parameters
10 TYPE_USE Uses of a type

如果一个注解没有 @Target 限制,可以用于任何地方。

@Retention

@Retention 注解用于表示一个注解保存到哪个阶段, RetentionPolicy 枚举类提供了以下 3 种级别:

Retention Policy Description
1 SOURCE Annotation are not included in calss files
2 CLASS Annotations are included in class files, but the jvm need not load them
3 RUNTIME Annotations are included in class files and loaded by the jvm. They are available through the reflection API

默认是 RetentionPolicy.CLASS

@Documented

@Documented 注解用于如 Javadoc 之类的文档生成工具,这里不展开解释。

@Inherited

@Inherited 注解仅作用于类,默认情况下,父类的普通注解不能被子类继承,但如果父类的注解被 @Inherited 注解修饰,则该注解可以被子类自动继承。

@Repeatable

Java 8 新增了一个 @Repeatable 元注解,使得某些注解可在同一元素上修饰多次。@Repeatable 元注解需要传入一个 container annotation

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Repeatable(Tags.class) // 需要传入一个 container annotation
  4. public @interface Tag {
  5. String value() default "";
  6. }
  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface Tags { // container annotation
  4. Tag[] value(); // 返回类型必须是对应的注解数组,方法名必须是 value
  5. }

使用自定义的 @Tag 注解并测试,代码如下所示:

  1. public class AnnotationClass {
  2. @Tag("father")
  3. @Tag("father") // 注意标注了两次相同的值
  4. @Tag("teacher")
  5. public void getTags() {}
  6. }
  1. try {
  2. Method method = AnnotationClass.class.getDeclaredMethod("getTags");
  3. Tag tag = method.getAnnotation(Tag.class); // null
  4. Tags tags = method.getAnnotation(Tags.class);
  5. // [@Tag(value=father), @Tag(value=father), @Tag(value=teacher)]
  6. System.out.println(Arrays.toString(tags.value()));
  7. } catch (NoSuchMethodException e) {
  8. e.printStackTrace();
  9. }
  10. }

对于上述代码需要主语两点:

  • 直接用反射获取被 @Repeatable 修饰的注解(如 @Tag )返回值为 null
  • @Repeatable 注解不会过滤相同的值。