原文地址: Lesson: Annotations
注解
注解(Annotations)是一种类型的元数据,提供和一个程序相关的数据,但是它却不是程序本身,对于程序的代码执行也没有直接影响。
注解有以下几种用途:
- 给编译器提供信息 - 编译器可以利用注解来检测错误和取消警告(suppress warnings)。
- 编译时和部署时处理 - 工具软件可以处理注解信息来生成代码,XML文件,等等
- 运行时处理 - 某些注释可以在运行时被处理
这一教程解释了注解可以被用在哪些地方:如何使用注解;Java 平台有哪些可用的预定义注释;type注释如何与类型可插拔系统一起协作写出支持强类型检查的代码;如何实现可复用的注解。
注解基础知识
注解的格式
最简单的注解如下:
@Entity
@
符号向编译器表面后面跟着的是一条注解。在下面的示例中,Override
是一个注解:
@Override
void mySuperMethod() {... }
注解也可以包含额外的元素,这些元素可以带名称,也可以不带名称:
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }
或者
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }
如果只有一个元素,那么该元素的名称可以省略,它的名称将会被设置为 value
:
@SuppressWarnings("unchecked")
void myMethod() { ... }
如果注解中没有元素,那么括弧也可以省略(如@Override
的例子)。
在一个声明中使用多个注解也是可以的:
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
如果多个注解具有相同的类型,就称之为重复注解(repeating annotation)
重复注解是Java SE 8 才支持的,查看Repeating Annotations 了解更多关于重复注解的信息。
注解的类型可以是java.lang 或者 java.lang.annotation 包中定义的一个类型。 在之前的例子中,Override
和 SuppressWarnings
是 Java预定义的注解(预定义的Java注解),你也可以定义你自己的注解类型,之前示例中的Author
和Ebook
注解就是自定义的注解。
注解可以被用在什么地方
注解可以被用在声明上,如类声明,类属性声明,方法声明,还有其他的程序元素。 当注解应用于声明是,一个注解通常占据单独的一行。
在JavaSE 8 Release 中,注解也可以被用在类型的使用上,如:
-
类实例创建表达式:
new @Interned MyObject();
-
类型转换:
myString = (@NonNull String) str;
-
implements
子句:class UnmodifiableList
implements @Readonly List<@Readonly T> { ... } -
异常抛出语句:
void monitorTemperature() throws @Critical TemperatureException { ... }
这些格式的注解被称之为类型注解。 你可以查看 Type Annotations and Pluggable Type Systems 了解更多信息。
声明注解类型
许多注解用于替换代码中的注释。
设想有一个软件组织在每个类的内容开始部分使用注释提供一些重要的信息,如下:
public class Generation3List extends Generation2List {
// Author: John Doe
// Date: 3/17/2002
// Current revision: 6
// Last modified: 4/12/2004
// By: Jane Doe
// Reviewers: Alice, Bill, Cindy
// class code goes here
}
为了使用注解来添加相同的元数据,首先你需要有一个 注解类型。 可以通过如下格式定义:
@interface ClassPreamble {
String author();
String date();
int currentRevision() default 1;
String lastModified() default "N/A";
String lastModifiedBy() default "N/A";
// Note use of array
String[] reviewers();
}
注解的定义和接口的定义方式很像,不过注解的 interface
关键词前面有一个(@
)。 实际上注解类型也是一种interface
,这个在后面会说明,但是现在你并不需要理解这一点。
示例中的注解定义包含了注解类型元素 定义,它看起来很像一个方法,但是它可以定义默认值。
在定义了注解类型之后,你就可以使用它了,如:
@ClassPreamble (
author = "John Doe",
date = "3/17/2002",
currentRevision = 6,
lastModified = "4/12/2004",
lastModifiedBy = "Jane Doe",
// Note array notation
reviewers = {"Alice", "Bob", "Cindy"}
)
public class Generation3List extends Generation2List {
// class code goes here
}
提示: 为了使@ClassPreamble
中的信息可以出现在生成的java doc 中,你必须在@ClassPreamble
的定义前加上@Docmented
注解:
// import this to use @Documented
import java.lang.annotation.*;
@Documented
@interface ClassPreamble {
// Annotation element definitions
}
预定义的注解类型
Java SE API 中已经预定义了一系列的注解类型。其中一些被java编译器读取使用,还有一些用于其他的注解。
被Java 语言使用的注解
java.lang
包里定义了以下注解: @Deprecated
,@Override
,@SupressWarnings
。
@DePrecated @Deprecated
注解表示被其标记的元素已经废弃,不应该再被使用。 当使用一个被@Deprecated
标记的类/方法/变量时,编译器会生成一个警告。 当一个元素被废弃时,在它的Javadoc 注释中也应该使用@Deprecated
注解标记,如下面的示例代码所示. Javadoc中使用的@
符号和注解中使用@
符号并非巧合,实际上它们是相关联的概率。 另外,请注意 javadoc中@
后的内容以一个小写的d开头,而注解中的是大写的 D。
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
}
@Override @Override
注解告诉编译器被注解的元素覆盖了超类的对应的声明。(方法覆盖在 Interfaces and Inheritance 中说明)
// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }
但是,在覆盖方法时并不强制要求使用这个注解,它只是用来预防错误。 如果一个方法被@Override
标记却没有正确的覆盖超类的对应方法,那么编译器就会生成一个错误。
@SupressWarnings @SupressWarnings
注解禁止编译器产生本来应该产生的警告。下面的例子使用了一个被抛弃的方法,所以编译器会产生一条警告,但是 @SupressWarnings 注解能禁止编译器产生这条警告。
// use a deprecated method and tell
// compiler not to generate a warning
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}
每一个警告都属于一个类别,而Java 语言规范列出了两种警告类别:
deprecation
和 unchecked
,在与泛型出现之前旧代码交互时可能会出现未检查的警告。
你也可以在一个@SuppressWarnings
注解中禁止多个种类的警告:
@SuppressWarnings({"unchecked", "deprecation"})// 注意其中的"{}"
@SafeVarags 当@SafeVarags
注解被用于方法或者构造函数时,表示代码没有对 varargs
参数做潜在的不安全操作,当使用这个注解时, 和varargs
相关的unchecked 警告将会被禁止。
@FunctionalInterface Java SE8 引入了@FunctionalInterface
注解,用于指示指定的元素是Java语言规范定义的功能接口(Functional interface)。
应用在其他注解上的注解
应用在其他注解上的注解被称之为 元注解(meta_annotations),java.lang.annotation
中定义了一些元注解:
@Retention @Retention
注解指定被标记的注解如何存储:
-
RetentionPolicy.SOURCE
- 只在源代码中保存,而编译器会忽略 -
RetentionPolicy.CLASS
- java 编译器在编译时保存,但JVM会忽略。 -
RetentionPolicy.RUNTIME
- JVM 会保存,因此可以在运行时环境中使用(runtime environment)
@Documented @Documented
注解指示JavaDoc工具处理时被标记的元素应该生成文档。(默认情况下注解不会包包含到javadoc中),你可以查看 Javadoc tools page 获取更多信息。
@Target @Target
注解用于限制其他注解可以使用的元素,可以使用的值如下:
-
ElementType.ANNOTATION_TYPE
可以使用在任何元素上 -
ElementType.CONSTRUCTOR
使用于构造函数 -
ElementType.FIELD
类成员或属性 -
ElementType.LOCAL_VARIABLE
本地变量 -
ElementType.METHOD
方法级别的注解 -
ElementType.PACKAGE
包声明 -
ElementType.PARAMETER
方法的参数 -
ElementType.TYPE
一个类的任何元素 (any element of a class)
@Inherited @Inherited
默认情况下子类是没有继承父类的注解信息的,而使用此注解后,子类会继承父类的注解。使用了@Inherited
注解后,当用户查询类中的注解信息时,如果没有找到相应类型的注解信息,则会去父类中查询。@Inherited
注解只用于类声明上的注解。
@Repeatable @Repeatable
注解时 Java SE 8 引入的, 表被声明的注解可以在一个元素声明上使用多次。更多信息可以查看 Repeating Annotations。
关于@FunctionalInterface注解:
- Java 8函数式接口functional interface的秘密
- 深入浅出 Java 8 Lambda 表达式