清单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的类。这个接口没有成员,它的名字package-info不是一个合法的java标识Q所以它不能用在java源代码中。这个接口的存在只是单的被看作一个ؓpackage annotation准备的占位符?br>
用于修饰Ҏ参数、catch参数、本地变量的annotation只是单的出现在这些程序成员的修饰W位|。javacL件格式没有ؓ本地变量或者catch参数存储annotation作准备,所以这些annotationL保留在源代码U别Qsource 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ƈ间接地被MethodcRConstructorcRjava.lang.reflect的Fieldcd现。而annotation中的Ҏ参数可以通过MethodcRConstructorcȝ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>
Annotationcd的方法必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。这个annotationcd一个数l作Z唯一的成员。数l中的每个元素都是异常类。ؓ了加强对未检查的异常Q此cd帔R是在q行时抛出)q行报告Q我们可以在代码中对异常的类型进行限Ӟ
清单13Q?/strong>
public @interface UncheckedExceptions {
Class<? extends RuntimeException>[] value();
}
五、Meta-Annotation
Annotationcd可以被它们自己所标注。Java5.0定义?个标准的meta-annotationcdQ它们被用来提供对其它annotationcd作说明。这些类型和它们所支持的类在java.lang.annotation包中可以扑ֈ。如果需要更详细的信息可以参考jdk5.0手册?br>
1Q再谈Target
作ؓmeta-annotationcd的Target,它描qCannotation所修饰的程序成员的cd。当一个annotationcd没有TargetӞ它将被作为普通的annotation看待。当它修饰一个特定的E序成员Ӟ它将发挥其应用的作用Q例如:Override用于修饰ҎӞ增加了@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文gQ如果保留在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则q个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的最大快乐?br>
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=658824

]]>