本文基于 Java 8。
概述
注解类型(annotation type)是一种标记,将信息与程序结构关联起来,但在运行时不会产生任何影响。注解表示的是注解类型的具体调用,并通常会为该类型的元素提供值。
注解类型是一种特殊接口类型,是元数据(metadata)的一种形式。
注解类型可应用于声明或任何类型使用时(Java 8 新增),使用的标准形式如下:
1 | // “@”暗示编译器其后跟着的是注解类型的名称 |
使用示例:
1 | // 普通注解的使用 |
功能
注解的功能主要包括:
- 为编译器提供信息——编译器可以用其来探测错误或抑制警告;
- 编译时和部署时处理——软件工具可以处理注解用以生成代码,XML 文件等;
- 运行时处理——运行时检查某些注解。
简单说来,注解常用于:创建文档、跟踪代码的依赖性、执行编译时格式检查、代替已有的配置文件。
分类
注解类型可以按不同的依据划分为不同的类型,主要的分类方式有以下一些。
按元素多少可分为:完整(普通)注解、单元素注解、标记注解。
按注解保留的生命周期可分为:源代码注解、编译时注解、运行时注解。
按来源可为分为:预定义注解、第三方注解、自定义注解。
预定义注解
Java 本身内置了一些注解类型供我们使用,称为“预定义注解”。
预定义注解在 java.lang
或 java.lang.annotation
包中。
其中有一部分是应用于注解类型的注解类型,称为“元注解”,这将在后续相应章节讲解。
下面列举说明的是除元注解外主要的预定义注解,它们大都是为编译器提供信息的。
@Deprecated
@Deprecated
暗示被标注的元素已过时并不应再使用。
当程序使用一个被该注解标记的类型、方法、域或构造器的声明时,编译器将产生一个警告。 “使用”包括:名字覆盖、调用、引用。
唯一可以引发过时警告的隐式声明的结构就是容器注解,但不鼓励这样的标注。
过时的元素的 Javadoc 注释中也应该使用 @deprecated 标记。
1 | // Javadoc comment follows |
@Override
@Override
表示被标注的方法覆盖了超类型的方法。
如果被标注的方法并非覆盖,则编译器将产生一个错误。
@SuppressWarnings
@SuppressWarnings
告知编译器抑制指定的警告。
我们必须指定一种或几种要抑制的警告,抑制多种警告语法像这样:@SuppressWarnings({"unchecked", "deprecation"})
@SafeVarargs
@SafeVarargs
明确其代码不存在潜在的安全风险,抑制相关警告。
具有不可具化元素类型的可变参数可能会引发堆污染,并产生编译时非受检警告。 如果可变参数方法体相对于可变参数是行为得当的,那么应当抑制这种警告。
1 | public static<T> Boolean addAll(Collection<? super T> c, T... elements) |
只能应用于 static
方法、final
实例方法,以及构造器,所以不能在会发生方法覆盖的地方使用。
Java 7 新增注解。
@FunctionalInterface
@FunctionalInterface
暗示类型声明是一个函数接口(functional interface)。
Java 8 新增注解。
元注解
应用于其它注解类型声明上的注解类型称为元注解(meta-annotations),主要定义在 java.lang.annotation
包中。
@Retention
@Retention
指定注解如何保留。
有 3 种策略:SOURCE
、CLASS
(缺省)、RUNTIME
,它们由 RetentionPolicy
枚举提供。详见附录。
@Target
@Target
指定注解类型可应用的上下文,包括声明上下文和类型上下文。
缺省 @Target
,注解类型可应用于除类型参数声明之外的所有声明上下文中,但不能应用于任何类型上下文中。
必须指定一个或多个目标元素,使用 ElementType
枚举指定,且不能重复。
1 | // 不指定目标类型是非法的 |
@Inherited
@Inherited
表明注解类型可以从超类继承,默认不是继承的。
仅能应用于类声明,否则无效。仅促成从超类继承,对实现的接口无效。
派生类上查询不到的可继承注解,应在超类上查询。
@Documented
@Documented
表明使用 Javadoc 工具时,注解应该文档化。(默认注解不包括在 Javadoc 中)
@Repeatable
@Repeatable
在可重复注解类型声明上表示其容器注解类型。
Java 8 新增。
定义
语法
1 | AnnotationTypeBody: |
按照
AnnotationTypeElementDeclaration
产生式,在注解类型声明中的方法声明不能够有任何形式参数、类型参数或 throws 子句。按照
AnnotationTypeElementModifier
产生式,在注解类型声明中的方法声明不能是default
或static
的。按照惯例,注解是唯一可以出现在注解类型元素上的
AnnotationTypeElementModifier
。
示例:
1 |
|
注解类型,是接口的一种特殊表现形式,所以与接口定义类似,只是在关键字 interface
前加“@
”区分普通接口。
注解类型的直接超接口是 java.lang.annotation.Annotation
。
注解类型元素
注解类型声明 T
不能直接或间接地包含 T
类型的元素。
注解类型的任何元素都是使用在该类型中显式声明的方法定义的,并且以无参无异常的方式声明。
元素的返回值类型是受限的,只能是基本数据类型、String
、Class
或 Class
的调用、Annotation
、Enumeration
及它们的数组。
元素方法签名不能跟 Object
类或 Annotation
接口中声明的任何 public
或 protected
方法签名是覆盖等价的。
可以用 default
为元素指定默认值。
查询获取
通过反射获取类、方法或成员上的运行时注解信息,从而实现动态控制程序运行的逻辑。
AnnotatedElement
接口提供了获取注解信息的相关方法,Class
、Method
、Field
等都实现了该接口,所以可以获取到其上的注解信息。
1 | boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) |
注意事项
局部变量声明上的注解永远不会在二进制表示中保留。*
因为语法限制,注解类型声明不能是泛化的。
java.lang.annotation.Annotation
是注解类型的超接口,但它本身不是注解类型。
注解类型元素的缺省值并没有编译到注解中,而是在注解被读取时动态地应用的。因此,改变缺省值会影响注解。
注释类型可作为注解出现在自身类型声明上。注解关系传递闭包中可以存在环。
1 | // 以下 2 个注解类型循环注解对方,甚至自注解都是合法的 |
注解类型声明 T
不能直接或间接地包含 T
类型的元素。
1 | // 元素类型是自身类型是不允许的 |
新特性
说明 Java 7 和 Java 8 所加入的与注解相关的新特性。
新增注解类型
Java 7 新增 @SafeVarargs
注解类型;Java 8 新增 @FunctionalInterface
注解类型。相关说明见上文。
类型注解
Java8 之前,注解类型只能应用于声明,之后可应用于任何类型使用时,即在任何使用一个类型的地方, 比如:类实例化表达式(new
)、类型转换、implements
子句、throws
子句。这称为类型注解(type annotation)。
应用于类型使用的注解类型,称为类型注解(type annotation)。
类型注解增强了类型检查,不过 Java 8 没有提供检查框架,但是可以自定义或下载一个。
示例:
1 | 类实例创建表达式: new @Interned MyObject(); |
可重复注解
Java 8 之前,同一个注解类型只能应用一次,之后如果可以同时应用多次,则称为“可重复注解(repeating annotation)”。
定义可重复注解类型跟普通注解类型定义相似,不同仅在于需要使用 @Repeatable
元注解标记。
1 |
|
我们可以注意到 @Repeatable
元注解需要指定一个 Class
类型的元素,该类型称为可重复注解类型的“容器注解类型(containing annotation type)”。
定义容器注解类型也有点特别,它必须定义一个 value 元素,其类型为上面定义的可重复注解类型的数组。
1 | public @interface MyContainingType |
定义可重复注解还有以下一些需要注意的事项:
- 容器注解类型声明的任何除
value()
外的方法都应有缺省值。 - 容器注解类型存留时间至少与其对应的可重复注解类型一样长。存留期由
@Retention
注解表示。 - 可重复注解类型可应用的程序元素种类至少与其对应的容器注解类型相同。
- 可重复注解类型有
@Documented
/@Inherited
(元)注解,则容器注解类型也有。 - 容器注解类型自身可以是可重复的注解类型。
附录
定义一览
预定义注解
Java 内置的注解。
元注解
应用于其它注解的注解。
标记注解
没有任何元素的注解类型。
单元素注解类型
只有一个元素的注解类型。
类型注解
应用于类型使用的注解。
可重复注解
可以重复应用多次的注解。
相关规范
注解应用于声明上时,通常每个注解单独一行。
@
和 interface
是 2 个不同的标记。技术上可以用空白字符分隔开,但不推荐。
单元素注解类型唯一的元素应取名为 value,以便在使用时可以忽略元素名和赋值号(=
)。
注解中的元素-值对出现的顺序应该与注解类型声明中对应元素出现顺序相同。
预定义注解参考
名称 | 描述 | 保留 | 目标 |
---|---|---|---|
@Deprecated | 过时、已弃用 | RUNTIME | CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE |
@Override | 覆盖超类型方法 | SOURCE | METHOD |
@SuppressWarnings | 抑制警告 | SOURCE | TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE |
@SafeVarargs | 消除泛型可变参数警告 | RUNTIME | CONSTRUCTOR, METHOD |
@FunctionalInterface | 函数接口 | RUNTIME | TYPE |
@SuppressWarnings 抑制警告的关键字参考
关键字 | 说明 |
---|---|
all | to suppress all warnings (抑制所有警告) |
boxing | to suppress warnings relative to boxing/unboxing operations(抑制装箱、拆箱操作时候的警告) |
cast | to suppress warnings relative to cast operations (抑制类型转换操作相关的警告) |
dep-ann | to suppress warnings relative to deprecated annotation(抑制启用注释的警告) |
deprecation | to suppress warnings relative to deprecation(抑制过时方法警告) |
fallthrough | to suppress warnings relative to missing breaks in switch statements(抑制 switch 语句中缺失 breaks 的警告) |
finally | to suppress warnings relative to finally block that don’t return (抑制 finally 模块没有返回的警告) |
hiding | to suppress warnings relative to locals that hide variable |
incomplete-switch | to suppress warnings relative to missing entries in a switch statement (enum case)(忽略不完整的 switch 语句) |
nls | to suppress warnings relative to non-nls string literals(忽略非 nls 格式的字符) |
null | to suppress warnings relative to null analysis(忽略对 null 的操作) |
rawtypes | to suppress warnings relative to un-specific types when using generics on class params(使用 generics 时忽略没有指定相应的类型) |
restriction | to suppress warnings relative to usage of discouraged or forbidden references |
serial | to suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在 serializable 类中没有声明 serialVersionUID 变量) |
static-access | to suppress warnings relative to incorrect static access(抑制不正确的静态访问方式警告) |
synthetic-access | to suppress warnings relative to unoptimized access from inner classes(抑制子类没有按最优方法访问内部类的警告) |
unchecked | to suppress warnings relative to unchecked operations(抑制没有进行类型检查操作的警告) |
unqualified-field-access | to suppress warnings relative to field access unqualified (抑制没有权限访问的域的警告) |
unused | to suppress warnings relative to unused code (抑制没被使用过的代码的警告) |
元注解参考
名称 | 描述 |
---|---|
@Retention | 保留级别 |
@Target | 可应用上下文 |
@Inherited | 可继承性 |
@Documented | 文档化 |
@Repeatable | 可重复注解 |
注:元注解都将保留到运行时,当然,仅能应用于注解类型。
注解的保留策略参考
枚举值 | 说明 |
---|---|
RetentionPolicy.SOURCE | 源代码级保留,编译器将忽略 |
RetentionPolicy.CLASS | (默认)在编译时被编译器保留,但是 JVM 将忽略 |
RetentionPolicy.RUNTIME | 被 JVM 保留,运行时可用 |
注解应用目标参考
枚举值 | 说明 |
---|---|
ElementType.ANNOTATION_TYPE | 注解类型 |
ElementType.CONSTRUCTOR | 构造器 |
ElementType.FIELD | 域 |
ElementType.LOCAL_VARIABLE | 局部变量 |
ElementType.METHOD | 方法 |
ElementType.PACKAGE | 包 |
ElementType.PARAMETER | 方法参数 |
ElementType.TYPE | 任何类元素,包括类、接口、枚举 |
ElementType.TYPE_PARAMETER | 泛化类、接口、方法和构造器的类型参数声明 |
ElementType.TYPE_USE | 16 种类型上下文 |
参考文档
Oracle官方文档《The Java™ Tutorials》