基本参考自Java Annotation Tutorial doc:http://docs.oracle.com/javase/tutorial/java/annotations/index.html, 但因为不是全文翻译,也加了些自己的思考,所以选了原创
1. Annotation
Java Annotation是一种元数据(metadata),不是Java程序(逻辑)的一部分。Annotation有以下用途:
1.1 为编译器提供信息: 编译器可以根据annotation检查Error或者忽略Warning
1.2 编译和发布时信息处理: 一些工具可以利用Java code里的annotation信息生成必要的code、XML文件等等,比如Java doctools
1.3 运行时信息处理:一些annotation可以在运行是被读取,根据这些annotation,Java程序可以更加灵活的控制流程(比如TestNG里使用了大量运行时可检查的annotation)
2. Annotation基础
2.1 Annotation格式
Annotation用@符号标识,简单的比如:
@Override
void overrideFunction() { ... }
Annotation可以带元素(Elements),元素放在小括号里,有element name和element value,比如:
@Author (
name="navy",
date="2014/10/13"
)
class TestClass { ... }
<pre name="code" class="java">@SupressWarnings(value="unchecked")
void deprecatedMethod { ... }
如果annotation只带一个元素,可以不用知道元素名,只提供元素的值即可
@SupressWarnings("unchecked")
void deprecatedMethod { ... }
可以同时用多个annotation:
@Author(name = "Jane Doe")
@EBook
class MyClass { ... }
同一个annotation如果声明为repeating annotation,则可以同时在一个地方使用多次(没有声明的编译器会报错):
@Author(name = "Jane Doe")
@Author(name = "John Smith")
class MyClass { ... }
Annotation的类型可以是以下两个package中的任一类型:
java.lang
or
java.lang.annotation,你也可以创建自己的annotation类型
2.2 什么地方可以使用Annotation
类、域、方法声明以及其他程序元素,当用在声明上的时候,通常annotation都写在单独的行内
在Java SE 8中,annotation还可以用于类型(Type)上,这种类型叫类型Annotation(Type Annotation),比如:
- 创建类实例:
new @Interned MyObject();
- 类型上溯:
myString = (@NonNull String) str;
implements
语句:class UnmodifiableList<T> implements @Readonly List<@Readonly T> { ... }
- 违例声明:
void monitorTemperature() throws @Critical TemperatureException { ... }
3. 声明(创建)一个Annotation类型
比如我们要为每一个类添加一个annotation type,记录这个类的作者等信息:
@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();
}
上例可以看到,Annotation类型的定义跟接口(Interface)有点类似,只不过在interface关键字前加了个@符号。实际上Annotation类型是接口的一种形式。
看看Annotation的内容,里面包含了annotation类型元素(Annotation Type Element)的声明,有点像方法,但是没有方法体。可以为这些元素提供一个默认值。定义好就可以使用这个Annotation type了:
@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
}
import java.lang.annotation.*
@Documented
@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();
}
4. 预定义的annotation类型
Java中预定义了一些annotation types,一些是编译器相关的,一些是用在别的Annotation类型上的(又叫meta-annotation)
4.1 用于编译器中的annotation类型(java.lang中定义):@Deprecated, @Override, @SuppressWarnings
@Deprecated 使用这个annotation的元素(class,method,field)都是不推荐再使用的,如果在code中使用编译时会有warning。而且当一个元素被抛弃(不再使用),除了使用@Deprecated annotation外,还要添加javadoc的@deprecated annotation(注意这两个不一样,首字母一个大写一个小写):
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
}
// mark method as a superclass method
// that has been overridden
@Override
int overriddenMethod() { }
@SuppressWarnings 告诉编译器忽略指定的warning,Java有两种类型的warning:deprecation和unchecked,下例忽略deprecation的warning
// use a deprecated method and tell
// compiler not to generate a warning
@SuppressWarnings("deprecation")
void useDeprecatedMethod() {
// deprecation warning
// - suppressed
objectOne.deprecatedMethod();
}
@SafeVarargs 在方法或构造器上使用时,表明方法内没有对Varargs执行潜在的不安全操作,使用这个annotation类型时,Varargs相关的unchecked warning会被忽略
@FunctionalInterface 表明声明应该是一个功能性的接口,这是Java SE 8新引入的
4.2 用在其他annotation类型上的annotation(meta-annotation): 在java.lang.annotation中定义
@Retention 指出annotation存在于哪个level
RetentionPolicy.SOURCE
– 只存在于源码level,编译器看不见RetentionPolicy.CLASS
– 只有编译时被编译器使用,JVM看不见RetentionPolicy.RUNTIME
– 运行时环境里也可以看见,可以被JVM使用
@Target 限制哪些Java元素可以使用这个annotation,它指定下列之一作为value
ElementType.ANNOTATION_TYPE
可用于annotation类型ElementType.CONSTRUCTOR
用于构造器ElementType.FIELD
用于域或者属性ElementType.LOCAL_VARIABLE
用于本地变量ElementType.METHOD
用于方法ElementType.PACKAGE
用于包声明ElementType.PARAMETER
用于方法的参数ElementType.TYPE
用于类的任何元素
@Repeatable Java SE 8中新加的,被标记的annotation类型可以在同一个元素上使用多次
5. 类型Annotation及便携式类型系统
Java SE 8之前,annotation只能应用在声明中(方法,类,域),但是现在annotation可以用于任何的使用类型的地方(Type Use),参见2.2中的实例。
类型annotation被用来强化Java程序分析中的类型检查,它让用户可以自己写一个(或使用一个别人的)类型检查框架,与编译器配合使用。
比如想保证一个变量绝对不会为null,你可以写一个plug-in来检查,在code中给那个变量添加一个annotation指明它不能为null:
@NonNull String str;编译时,将NonNull模块包含到命令行中,编译器在检查到潜在的问题是会报warning,你可以修改代码防止错误发生。
你可以使用多个类型检查的模块,每个模块检查不同的错误,这样就可以在编译时添加特定的检查以避免相应错误,同时帮助你写出更健壮不易出错的代码。
现在有很多的第三方的类型检查模块可以使用,比如Checker Framework created by the University of Washington
更多内容可参考:http://types.cs.washington.edu/checker-framework/
6. 重复Annotation(Repeating Annotations)
有时候我们想在同一个声明或者类型上重复使用相同的Annotation类型,这时需要用到重复Annotation。
@Alert(role="Manager")
@Alert(role="Administrator")
public class UnauthorizedAccessException extends SecurityException { ... }
考虑到兼容性问题,重复annotations被存放在一个编译器自动生成的container annotation中,为此你需要以下两个声明:
6.1 声明一个重复的annotation类型: 必须标识为@Repeatable,括号中指明了这个annotation类型的container annotation,Schedule的container annotation就是Schedules,所以@Schedule annotations就被存放在一个@Schedules annotation中
import java.lang.annotation.Repeatable;
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
6.2 声明container annotation类型
container annotation类型必须有一个array类型的,名为value的元素,且这个array类型的component type必须是要用到这个container annotation的repeatable annotation类型:
public @interface Schedules {
Schedule[] value();
}
7. 获取annotation
反射(Reflection)API里有几种方法可以获取annotation相关信息,比如AnnotatedElement.getAnnotationByType(Class<T>), AnnotatedElement.getAnnotations(Class<T>)。
跟多获取annotation的方法见:http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AnnotatedElement.html