清单5Q?/strong>
@Reviews({ // Single-value annotation, so "value=" is omitted here
@Review(grade=Review.Grade.EXCELLENT,
reviewer="df"),
@Review(grade=Review.Grade.UNSATISFACTORY,
reviewer="eg",
comment="This method needs an @Override annotation")
})
annotation语法的另一个重要规则是没有E序成员可以有多于一个的同一annotation实例。例如在一个类中简单的攄多个@Review annotation。这也是在上面代码中定义@Reviews annotationcd数组的原因?br />
4。Annotation成员cd和|
annotation成员必须是非I的~译时常量表辑ּ。可用的成员cd为:primitivecd? String, Class, enumeratedcd, annotationcd, 和前面类型的数组?br />
下面我们定义了一个名为UncheckedExceptions 的annotationcdQ它的成员是一个扩展了RuntimeExceptioncȝcLl?br />
清单6Q?/strong>
@UncheckedExceptions({
IllegalArgumentException.class, StringIndexOutOfBoundsException.class
})
5。Annotation的目标:
annotation通常被放在类型定义和成员定义的前面。然而它也出现在package、方法参数、本地变量的前面。下面,我们来讨Z下这些不大常用的写法Q?br />
package annotation出现在package声明的前面?br />
下面的例子package-info.java中不包含M的公q型定义,却包含一个可选的javadoc注释?br />
清单7Q?/strong>
/**
* This package holds my custom annotation types.
*/
@com.davidflanagan.annotations.Author("David Flanagan")
package com.davidflanagan.annotations;
当package
-info.java文g被编译时Q它生名为包含annotationQ特D的接口Q声明的package-info.class的类。这个接口没?
成员Q它的名字package-info不是一个合法的java标识Q所以它不能用在java源代码中。这个接口的存在只是单的被看作一个ؓ
package annotation准备的占位符?br />
用于修饰Ҏ参数、catch参数、本地变量的annotation只是单的出现
在这些程序成员的修饰W位|。javacL件格式没有ؓ本地变量或者catch参数存储annotation作准备,所以这些annotationL?
留在源代码别(source retentionQ;Ҏ参数annotation能够保存在类文g中,也可以在保留到运行时?br />
最后,h意,枚Dcd定义中不允许M的修饰符修饰其枚丑ր{?br />
6。Annotation和默认|
在Annotation
中,没有默认值的成员必须有一个成员倹{而如何理解默认值是如何被处理就是一个很重要的细节:annotationcd所定义的成员默认D存储?
class文g中,不被~译到annotation里面。如果我们修改一个annotationcd使其成员的默认值发生了改变Q这个改变对于所有此cd
的annotation中没有明提供成员值的成员产生影响Q即修改了该成员的成员|。即使在annotationcd使其成员的默认D改变?
annotation从没被重新编译过Q该cd的annotation(改变前已l被~译?也受到媄响?br />
三、Annotation工作原理Q?/span>
Annotation与反?/span>
在java5.0
中Java.lang.reflect提供的反API被扩充了dq行时annotation的能力。让我们回顾一下前面所讲的Q一?
annotationcd被定义ؓruntime
retention后,它才是在q行时可见,当class文g被装载时被保存在class文g中的annotation才会被虚拟机d。那?
reflect是如何帮助我们访问class中的annotation呢?
下文在java.lang.reflect用于
annotation的新Ҏ,其中java.lang.reflect.AnnotatedElement是重要的接口Q它代表了提供查?
annotation能力的程序成员。这个接口被java.lang.Package、java.lang.Class实现Qƈ间接地被MethodcR?
ConstructorcRjava.lang.reflect的Fieldcd现。而annotation中的Ҏ参数可以通过MethodcR?
ConstructorcȝgetParameterAnnotations()Ҏ获得?br />
下面的代码用了AnnotatedElementcȝisAnnotationPresent()Ҏ判断某个Ҏ是否h@Unstable annotationQ从而断a此方法是否稳定:
清单8Q?/strong>
import java.lang.reflect.*;
Class c = WhizzBangClass.class;
Method m = c.getMethod("whizzy", int.class, int.class);
boolean unstable = m.isAnnotationPresent(Unstable.class);
isAnnotationPresent ()Ҏ对于查marker annotation是十分有用的Q因为marker
annotation没有成员变量Q所以我们只要知道class的方法是否用了annotation修饰可以了。而当处理h成员?
annotationӞ我们通过使用getAnnotation()Ҏ来获得annotation的成员信息(成员名称、成员|。这里我们看C一
套优的java
annotationpȝQ如果annotation存在Q那么实C相应的annotationcd接口的对象将被getAnnotation()Ҏ
q回Q接着调用定义在annotationcd中的成员Ҏ可以方便地获得Q何成员倹{?br />
回想一下,前面介绍的@Reviews annotationQ如果这个annotationcd被声明ؓruntime retention的话Q我们通过下面的代码来讉K@Reviews annotation的成员|
清单9Q?/strong>
AnnotatedElement target = WhizzBangClass.class; //获得被查询的AnnotatedElement
// 查询AnnotatedElement的@Reviews annotation信息
Reviews annotation = target.getAnnotation(Reviews.class);
// 因ؓ@Reviews annotationcd的成员ؓ@Review annotationcd的数l,
// 所以下面声明了Review[] reviews保存@Reviews annotationcd的value成员倹{?br />
Review[] reviews = annotation.value();
// 查询每个@Review annotation的成员信?br />
for(Review r : reviews) {
Review.Grade grade = r.grade();
String reviewer = r.reviewer();
String comment = r.comment();
System.out.printf("%s assigned a grade of %s and comment '%s'%n",
reviewer, grade, comment);
}
四、如何自定义AnnotationQ?/span>
1Q详解annotation与接口的异同Q?/span>
因ؓannotationcd是一个非凡的接口Q所以两者之间存在着某些差异Q?br />
A.Annotationcd使用关键字@interface而不是interface?/span>
q个关键字声明隐含了一个信息:它是l承了java.lang.annotation.Annotation接口Qƈ非声明了一个interface?br />
B.Annotationcd、方法定义是独特的、受限制的?/span>
Annotation
cd的方法必d明ؓ无参数、无异常抛出的。这些方法定义了annotation的成员:Ҏ名成Z成员名,而方法返回值成Z成员的类型。而方法返?
值类型必Mؓprimitivecd、Classcd、枚丄型、annotationcd或者由前面cd之一作ؓ元素的一l数l。方法的后面可以使用
default和一个默认数值来声明成员的默认|null不能作ؓ成员默认|q与我们在非annotationcd中定义方法有很大不同?br />
Annotationcd和它的方法不能用annotationcd的参数、成员不能是generic。只有返回值类型是Class的方法可以在annotationcd中用genericQ因为此Ҏ能够用类转换各U类型{换ؓClass?br />
C.Annotationcd又与接口有着q似之处?/span>
它们可以定义帔R、静态成员类型(比如枚Dcd定义Q。Annotationcd也可以如接口一般被实现或者ѝ?br />
2Q实例:
下面Q我们将看到如何定义annotationcd的example。它展示了annotationcd声明以及@interface与interface之间的不同:
清单10Q?/strong>
package com.davidflanagan.annotations;
import java.lang.annotation.*;
/**
* 使用annotation来描q那些被标注的成员是不稳定的Q需要更?br />
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Unstable {}
下面的另一个example只定义了一个成员。ƈ通过这个成员命名ؓvalueQ我们可以方便的用这Uannotation的快捷声明方式:
清单11Q?/strong>
/**
* 使用Authorq个annotation定义在程序中指出代码的作?br />
*/
public @interface Author {
/** q回作者名 */
String value();
}
以下的example更加复杂。Reviews annotationcd只有一个成员,但是q个成员的类型是复杂的:由Review
annotationl成的数l。Review
annotationcd?个成员:枚Dcd成员grade、表CReview名称的字W串cd成员Reviewer、具有默认值的字符串类型成?
Comment?br />
清单12Q?/strong>
import java.lang.annotation.*;
/**
* Reviews annotationcd只有一个成员,
* 但是q个成员的类型是复杂的:由Review annotationl成的数l?br />
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Reviews {
Review[] value();
}
/**
* Review annotationcd?个成员:
* 枚Dcd成员grade?br />
* 表示Review名称的字W串cd成员Reviewer?br />
* h默认值的字符串类型成员Comment?br />
*/
public @interface Review {
// 内嵌的枚丄?br />
public static enum Grade { EXCELLENT, SATISFACTORY, UNSATISFACTORY };
// 下面的方法定义了annotation的成?br />
Grade grade();
String reviewer();
String comment() default "";
}
最后,我们来定义一个annotationҎ用于|列出类q行中所有的unchecked异常Q上文已l提到这U情况不一定是错误Q?
q个
annotationcd一个数l作Z唯一的成员。数l中的每个元素都是异常类。ؓ了加强对未检查的异常Q此cd帔R是在q行时抛出)q行报告Q我?
可以在代码中对异常的cdq行限制Q?br />
清单13Q?/strong>
public @interface UncheckedExceptions {
Class<? extends RuntimeException>[] value();
}
五、Meta-Annotation
Annotation
cd可以被它们自己所标注。Java5.0定义?个标准的meta-annotationcdQ它们被用来提供对其它annotationcd作说明?
q些cd和它们所支持的类在java.lang.annotation包中可以扑ֈ。如果需要更详细的信息可以参考jdk5.0手册?br />
1Q再谈Target
?
为meta-annotationcd的Target,它描qCannotation所修饰的程序成员的cd。当一个annotationcd没有
TargetӞ它将被作为普通的annotation看待。当它修饰一个特定的E序成员Ӟ它将发挥其应用的作用Q例如:Override用于修饰?
法时Q增加了@Targetq个meta-annotation׃ɾ~译器对annotation作检查,从而去掉修饰错误类型的Override?br />
Target meta-annotationcd有唯一的value作ؓ成员。这个成员的cd是java.lang.annotation.ElementType[]cd的,ElementTypecd是可以被标注的程序成员的枚Dcd?br />
2QRetention的用?/span>
?
们在文章的开头曾l提到过RetentionQ但是没有详l讲解。Retention描述了annotation是否被编译器丢弃或者保留在class?
Ӟ如果保留在class文g中,是否在class文g被装载时被虚拟机d。默认情况下Qannotation被保存在class文g中,但在q行时ƈ
不能被反访问。Retentionh三个取|source、class、runtimeQ这些取值来?
java.lang.annotation.RetentionPolicy的枚丄型倹{?br />
Retention meta-annotationcd有唯一的value作ؓ成员Q它的取值来自java.lang.annotation.RetentionPolicy的枚丄型倹{?br />
3QDocumented
Documented是一个meta-annotationcdQ用于描q其它类型的annotation应该被作标注的程序成员的公共APIQ因此可以被例如javadoc此类的工h档化?br />
Documented是一个marker annotationQ没有成员?br />
4QInherited
@Inherited
meta-annotation也是一个marker
annotationQ它阐述了某个被标注的类型是被承的。如果一个用了@Inherited修饰的annotationcd被用于一个classQ?
则这个annotation被用于该class的子cR?br />
注意Q@Inherited annotationcd是被标注q的class的子cLl承。类q不从它所实现的接口承annotationQ方法ƈ不从它所重蝲的方法承annotation?br />
?
得思考的是,当@Inherited
annotationcd标注的annotation的Retention是RetentionPolicy.RUNTIMEQ则反射API增强了这U
承性。如果我们用java.lang.reflectL询一个@Inherited
annotationcd的annotationӞ反射代码查将展开工作Q检查class和其父类Q直到发现指定的annotationcd被发玎ͼ
或者到辄l承l构的顶层?br />
六、ȝQ?/span>
本文几乎覆盖了所有的Annotation的概念和知识点,从annotation的定义、语法到工作原理、如何自定义 annotationQ直?
meta-
annotation。其中也h一些配套的代码片断可参考,虽然不是很多Q但是可谓言意赅、着光点,本h认ؓ用好annotation的关键还在于
使用。希望本手册能够帮助大家用好annotationQ这也是本h的最大快乐?

]]>