目录
一、介绍
注解(annotation),一种元数据,提供一些关于程序的数据,但不是程序的一部分,对程序的执行没有直接的影响。
注解有三种用处:提供信息给编译器,指导编译器行为;提供构建信息给构建工具,这些工具有ant、mavent等,构建工具会根据这些注解产生源码或其他文件;运行时提供信息,可以通过反射来获得这些注解信息。
定义的注解默认继承于java.lang.annotation.Annotation接口,因此你可以继承该注解,但是没多大的实际用处。
二、注解声明
@Retention(RetentionPolicy.RUNTIME)
@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();
}
注解声明与接口类似,多了个@。注解中的元素声明与接口方法类似,元素也可以赋予默认值,通过default实现。注解的元素类型只能是基本类型、数组、字符串、枚举、注解、Class。定义该注解时可以被其他元注解注释,如上面的@Retention,该注解说明自定义的注解ClassPreamble可以保存到运行时。
三、注解使用
注解一般可以使用在类、方法、字段等java元素上,这是在定义注解时声明的通过Target设置的。如:
@Author(
name = "Benjamin Franklin",
date = "3/27/2003"
)
class MyClass() { ... }
如果只有一个元素,该元素名字为value的情况下,名字可以忽略:
@SuppressWarnings(value = "unchecked")
void myMethod() { ... }
可以写成如下方式
@SuppressWarnings("unchecked")
void myMethod() { ... }
如果有多个元素,其他的有默认值,value元素没有,也可以忽略其他元素,和value名字:
@MyAnnotation("aaa")
class A<T,Y>{
}
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
public String value();
public String name() default "";
}
如果没有元素或者都有默认值,连括号也可以忽略:
@EBook
class MyClass { ... }
如果元素是数组,且赋予数组一个值,则不需要大括号:
@MyAnnotation("aaa")
class A<T,Y>{
}
@MyAnnotation({"bbb","ccc"})
class B{
}
@MyAnnotation("aaa","bbb")
class C{
}
@interface MyAnnotation{
String[] value();
String[] name() default "bbb";
}
四、预定义的注解类型
java se api中已经定义了一些注解,可以用于编译器和用于其他注解。
4.1、被编译器使用的注解
@Deprecated
告诉编译器被注解的元素是被弃用的,使用会被编译器警告。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
可以看出,@Deprecated可以被使用到很多地方,不局限于方法、类、字段。可以配合javadoc的@deprecated标签使用,通过该标签解释下为何弃用。
// Javadoc comment follows
/**
* @deprecated
* explanation of why it was deprecated
*/
@Deprecated
static void deprecatedMethod() { }
}
@Override
通知编译器被注释的方法必须覆盖父类方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
@SuppressWarnings
用于抑制警告
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
还有其他的如@SafeVarargs、@FunctionalInterface。
4.2、用于其他注解的注解
用于注释其他注解的注解称为元注解。
@Retention
指定被标记的注解如何被存储,比如存在源码中、字节码中、运行时中。如果@Retention不存在,则默认使用RetentionPolicy.CLASS策略,就是存在字节码中。
RetentionPolicy.SOURCE
– The marked annotation is retained only in the source level and is ignored by the compiler.RetentionPolicy.CLASS
– The marked annotation is retained by the compiler at compile time, but is ignored by the Java Virtual Machine (JVM).RetentionPolicy.RUNTIME
– The marked annotation is retained by the JVM so it can be used by the runtime environment.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
@Documented
被@Document注释的注解在其他地方被使用时,可以显示在javadoc导出的文档中。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
@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.
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
@Inherited
指示注解可以被继承。假设被@Inherited注释的注解为@A,如果父类被@A注释,子类便可以继承这个注解。但是并不完全对,因为通过反射,子类不能找到@A。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
@Repeatable
被@Repeatable注解的注解可以在一个地方使用多次,比如下面的@Schedule可以使用多次:
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanup() { ... }
但是由于兼容原因,repeatable注解(就是被@Repeatable注释过的注解)需要被存入容器注解(container annotation)。也就是说定义repeatable注解时还需要定义容器注解。下面声明repeatable注解:
@Repeatable(Schedules.class)
public @interface Schedule {
String dayOfMonth() default "first";
String dayOfWeek() default "Mon";
int hour() default 12;
}
@Repeatable中需要指定容器注解,容器注解的定义为:
public @interface Schedules {
Schedule[] value();
}
注意,容器注解的元素value数组中的类型一定要为repeatable注解类型。很拗口吧,但是既然要存入repeatable注解(也就是Schedule),当然要定义它的数组(Schedule[])。
五、其他
一些注解可以用于类型的使用上,被称为类型注解(type annotation),通常被用于类型检测。
参考:
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
http://tutorials.jenkov.com/java/annotations.html
https://blog.usejournal.com/how-much-do-you-actually-know-about-annotations-in-java-b999e100b929