Java Annotations
本文最后更新于 2024年3月19日 上午
Annotations, a form of metadata, provide data about a program that is not part of the program itself. Annotations have no direct effect on the operation of the code they annotate.
Annotations have a number of uses, among them:
- Information for the compiler — Annotations can be used by the compiler to detect errors or suppress warnings.
- Compile-time and deployment-time processing — Software tools can process annotation information to generate code, XML files, and so forth.
- Runtime processing — Some annotations are available to be examined at runtime.
Annotations Basics
The Format of an Annotation
最简单的注解形式如下所示:
@Entity
at 符号字符 ( @ ) 向编译器指示后面的内容是注解。
注解可以包含元素,这些元素可以命名或未命名,并且这些元素有值:
1 |
|
或者
1 |
|
如果只有一个名为 value 的元素,则可以省略该名称,如下所示:
1 |
|
如果注解没有元素,则可以省略括号,如:
1 |
|
也可以在同一个声明上使用多个注解:
1 |
|
如果声明的注解是相同的类型,则这称为重复注解:
1 |
|
从 Java SE 8 版本开始支持可重复注解(Repeating Annotations)。
注解类型可以是 Java SE API 的 java.lang 或 java.lang.annotation 包中定义的类型之一。在前面的示例中, Override 和 注解 是预定义的 Java 注解。也可以定义自己的注解类型。
Where Annotations Can Be Used
注解可以应用于声明:类、字段、方法和其他程序元素的声明。当在声明中使用时,按照惯例,每个注解通常出现在自己的行上。
从 Java SE 8 版本开始,注解也可以应用于类型的使用。
Class instance creation expression:
new @Interned MyObject();
Type cast:
myString = (@NonNull String) str;
implements clause:
1
2class UnmodifiableList<T> implements
@Readonly List<@Readonly T> { ... }Thrown exception declaration:
1
2void monitorTemperature() throws
@Critical TemperatureException { ... }
这种形式的注解称为类型注解。
Declaring an Annotation Type
许多注解取代了代码中的注释。
Suppose that a software group traditionally starts the body of every class with comments providing important information:
1 |
|
To add this same metadata with an annotation, you must first define the annotation type.
1 |
|
The body of the previous annotation definition contains annotation type element declarations, which look a lot like methods. Note that they can define optional default values.
After the annotation type is defined, you can use annotations of that type, with the values filled in, like this:
1 |
|
Predefined Annotation Types
Java SE API 中预定义了一组注解类型。一些注解类型由 Java 编译器使用,一些注解类型适用于其他注解。
Annotation Types Used by the Java Language
java.lang 中定义的预定义注解类型是 @Deprecated
、 @Override
和 @注解
。
@Deprecated
注解表示标记的元素已被弃用,不应再使用。每当程序使用带有 @Deprecated
注解的方法、类或字段时,编译器都会生成警告。当某个元素被弃用时,还应该使用 Javadoc @deprecated 标记对其进行记录,如以下示例所示。在 Javadoc 注释和注解中使用 at 符号 ( @ ) 并非巧合:它们在概念上是相关的。另请注意,Javadoc 标记以小写 d 开头,注解以大写 D 开头。
@Override
注解通知编译器该元素将覆盖超类中声明的元素。
虽然重写方法时不需要使用此注解,但它有助于防止错误。如果标有 @Override
的方法无法正确重写其超类之一中的方法,编译器会生成错误。
@SuppressWarnings
注解告诉编译器抑制否则会生成的特定警告。在以下示例中,使用了不推荐(@Deprecated
)使用的方法,编译器通常会生成警告。然而,在这种情况下,注解会导致警告被抑制。
1 |
|
每个编译器警告都属于一个类别。 Java 语言规范列出了两个类别: deprecation 和 unchecked 。当与泛型出现之前编写的遗留代码交互时,可能会出现 unchecked 警告。要抑制多个类别的警告,使用以下语法:
@SuppressWarnings({"unchecked", "deprecation"})
@SafeVarargs
注解应用于方法或构造函数时,断言代码不会对其 varargs 参数执行潜在的不安全操作。使用此注解类型时,将抑制与 varargs 使用相关的未经检查的警告。
Java SE 8 中引入的 @FunctionalInterface @FunctionalInterface 注解表明类型声明旨在成为 Java 语言规范所定义的函数式接口。
Annotations That Apply to Other Annotations
应用于其它注解的注解被称为元注解(meta-annotations)。java.lang.annotation 中定义了多种元注解类型。
@Retention
注解指定了标记的注解如何存储(保留的生命周期):
RetentionPolicy.SOURCE
– 标记的注解仅保留在源代码级别,并被编译器忽略。RetentionPolicy.CLASS
– 标记的注解在编译时由编译器保留,但被 Java 虚拟机 (JVM) 忽略。RetentionPolicy.RUNTIME
– 标记的注解由 JVM 保留,以便运行时环境可以通过反射机制使用。
@Documented
注解指示每当使用指定注解时,应使用 Javadoc 工具记录这些元素。
@Target
注解标记了另一个注解,以限制该注解可以应用于哪种 Java 元素。目标注解指定以下元素类型之一作为其值:
ElementType.ANNOTATION_TYPE
can be applied to an annotation type.ElementType.CONSTRUCTOR
can be applied to a constructor.ElementType.FIELD
can be applied to a field or property.ElementType.LOCAL_VARIABLE
can be applied to a local variable.ElementType.METHOD
can be applied to a method-level annotation.ElementType.PACKAGE
can be applied to a package declaration.ElementType.PARAMETER
can be applied to the parameters of a method.ElementType.TYPE
can be applied to any element of a class.
@Inherited
注解表示该注解类型可以从超类继承。 (默认情况下并非如此。)
@Repeatable
注解是 Java SE 8 中引入的,表示标记的注解可以多次应用于同一声明或类型使用。
Repeating Annotations
例如,正在编写代码来使用计时器服务,该服务能够在给定时间或按特定计划运行方法,类似于 UNIX cron 服务。现在,想要设置一个计时器来在每月的最后一天和每个星期五晚上 11:00 运行方法 doPeriodicCleanup 。要设置计时器运行,请创建 @Schedule
注解并将其应用到 doPeriodicCleanup 方法两次。第一次使用指定该月的最后一天,第二次指定星期五晚上 11 点,如以下代码示例所示:
1 |
|
出于兼容性原因,重复注解存储在 Java 编译器自动生成的容器注解中。为了让编译器执行此操作,代码中需要两个声明。
Step 1: Declare a Repeatable Annotation Type
注解类型必须使用 @Repeatable
元注解进行标记。以下示例定义了自定义 @Schedule
可重复注解类型:
1 |
|
括号中的 @Repeatable
元注解的值是 Java 编译器生成的用于存储重复注解的容器注解的类型。在此示例中,包含注解类型(containing annotation type)为 Schedules
,因此重复的 @Schedule
注解存储在 @Schedules
注释中。
将相同的注解应用于声明而不首先声明它是可重复的会导致编译时错误。
Step 2: Declare the Containing Annotation Type
包含注解类型必须具有数组类型的 value 元素。数组类型的元素类型必须是可重复的注解类型。包含注解类型的 Schedules 声明如下:
1 |
|