Java Annotation Tutorials

注:本文主要翻译自Lesson: Annotations,学习一下Java注解。

Annotations(注解)是一种元数据(metadata),用于描述一段程序但对程序的运行不产生直接影响。

注解有一些作用,其中包括:

  • 为编译器提供信息——注解可以被编译器用于探测 errors 或者 suppress warnings。
  • 编译时或部署时处理——软件处理注解后可生成代码,XML 文件等。
  • 运行时处理——一些注解可以用于运行时检查。

Annotations基础

注解的格式

注解最简单的格式如下:

1
@Entity

@字符告知编译器接下来是一个注解,下面这个例子是一个名为Override的注解:

1
2
@Override
void mySuperMethod() { ... }

注解还可以包含一些带命名或不带命名的元素,每个元素会有相应的 value:

1
2
3
4
5
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }

1
2
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }

如果只包含一个带命名的 value,那么这个 value 的 name 可以忽略:

1
2
@SuppressWarnings("unchecked")
void myMethod() { ... }

如果注解里没有元素,那么括号可以被省略,参考前面的@Override示例。

对于同一个声明,可以用多个注解:

1
2
3
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }

如果注解的类型相同,这种情况被称为重复注解(repeating annotation):

1
2
3
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }

重复注解自 Java 8 开始支持,详见 Repeating Annotations 部分。

注解的类型可以是java.langjava.lang.annotation包下面定义的类型,比如前面例子中的OverrideSuppressWarnings就是 Java 预置的注解类型。此外还可以自定义注解类型,如前面例子中的AuthorEbook

注解可以用在什么地方

注解可以用于声明类、域、方法和其他程序的元素,当用于声明时,注解往往自成一行。

从 Java 8 开始,注解还可作为类型(type),如下:

  • 创建类的实例:

    1
    new @Interned MyObject();
  • 转型:

    1
    myString = (@NonNull String) str;
  • 实现接口:
    1
    2
    class UnmodifiableList<T> implements
    @Readonly List<@Readonly T> { ... }
  • 抛异常的声明:
    1
    2
    void monitorTemperature() throws
    @Critical TemperatureException { ... }

这种形式的注解被称为类型注解(type annotation)。

声明一个注解类型

在代码中很多注解代替了注释(comments)。

比如,一个开发团队通常在一个类的 body 起始的位置添加一些重要的注释:

1
2
3
4
5
6
7
8
9
10
11
12
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
}

如果使用注解的话,首先要定义一个注解类型,语法如下:

1
2
3
4
5
6
7
8
9
@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前面多了个@

上面这个注解的定义中包括了注解类型和元素声明,其中 default 值是可选的。

定义好注解之后,我们可以像下面这样使用它:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@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
}

Note:为了让 Java Doc 包含注解@ClassPreamble的信息,我们需要在定义@ClassPreamble时为它再加上注解@Documented

1
2
3
4
5
6
7
8
9
10
>// import this to use @Documented
>import java.lang.annotation.*;
>@Documented
>@interface ClassPreamble {
> // Annotation element definitions
>}
>

预置的注解类型

Java API 中预定义了一些注解类型,一些用于编译器,还有一些用于其它注解。

Java使用的注解类型

java.lang中预置的注解有:@Deprecated, @Override, 和 @SuppressWarnings

@Deprecated注解表示被标记的元素是deprecated的,不建议再使用的元素。当程序使用了被@Deprecated注解的方法、类或域时,编译器会生成一个warning信息。当一个元素是deprecated,在Javadoc中也要使用@deprecated标记,Doc中的d小写,注解中是大写的D

1
2
3
4
5
6
7
8
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
}

@Override注解告诉编译器,该方法是重写自父类的方法:

1
2
3
4
// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }

@Override的好处是如果这个方法没用正确重写父类的方法时,编译器会产生一个 error 提示。

@SuppressWarnings注解提示编译器不用产生指定的 warning 信息,如调用一个 deprecated 方法时,编译器会生成一个 waring 提示,使用@SuppressWarnings注解后,这个 waring 就被抑制了:

1
2
3
4
5
6
7
8
// use a deprecated method and tell
// compiler not to generate a warning
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}

@SafeVarargs用于方法或构造器时,断言可变参数不会产生潜在的危险,由可变参数使用而产生的 warning 会被 suppressed。

@FunctionalInterface是Java 8引入的,表示函数式接口。

注解的注解

用于其它注解的注解,被称为元注解(meta-annotations),下面设计java.lang.annotation包里的相关注解。

@Retention指定了注解的保留方式:

  • RetentionPolicy.SOURCE – 被标记的注解只保留在源码级别,会被编译器忽略。
  • RetentionPolicy.CLASS – 被标记的注解保留至编译时,会被JVM忽略。
  • RetentionPolicy.RUNTIME – 被标记的注解被JVM保留,会用于运行时环境。

@Documented注解信息会被 Javadoc 工具写入文档。

@Target起限制作用,表示注解用于描述哪种 Java 元素:

  • ElementType.ANNOTATION_TYPE 用于描述注解
  • ElementType.CONSTRUCTOR 用于描述构造器
  • ElementType.FIELD 用于描述域
  • ElementType.LOCAL_VARIABLE 用于描述局部变量
  • ElementType.METHOD 用于描述方法的注解
  • ElementType.PACKAGE 用于描述包
  • ElementType.PARAMETER 用于描述参数
  • ElementType.TYPE 用于描述一个类的任何元素

@Inherited表示该注解是可以被继承的,只能用于声明类。

@Repeatable在Java 8引入,表示被标记的注解可以被重复用于同一个声明。


扩展阅读:

激情打赏