Java1.5 new feature --- Annotation 注释

Sun公司决定在Java的JDK 5版本中包括的另一个特性是元数据功能。这使得人们可以使用工具能够分析的额外信息来标记类,同时也可以自动向类应用特定代码块。元数据功能在java.lang. annotation包中被引入。注释是指Java中一个tag与构造(例如类,在注释术语中被称为目标)之间的关联。能够被注释的构造的类型在java.lang.annotation.ElementType枚举中列出,同时在下面的表1-3中列出。甚至注释也可以被注释。TYPE覆盖了类、接口和enum声明。

表1-3

 

ElementType常量

ElementType常量

ANNOTATION_TYPE

METHOD

CONSTRUCTOR

PACKAGE

FIELD

PARAMETER

LOCAL_VARIABLE

TYPE

另一个引入的概念是注释的生命周期,被称为保持(retention)。某些注释也许只是在Java源代码级别上才有用,例如javadoc工具的注释。其他的注释也许在程序正在执行时才需要。RetentionPolicy枚举列出了一个注释的3个type生命周期。SOURCE策略表示注释应该由编译器丢弃,也就是说,该注释只在源代码级别上有用。CLASS策略表示注释应该在类文件中出现,但是在运行时可能被丢弃。RUNTIME策略则表示注释在整个程序执行期间都应该有效,并且这些注释可以通过反射来查看。

在此包中定义了几个注释类型。表1-4中列出了这些类型。这些注释中的每一个都是从Annotation接口继承而来,该接口定义了equals方法和toString方法。

表1-4

 

注释类名称

描述

Target

指定一个注释类型可以应用的程序元素。每个程序元素只能出现一次

Documented

指定注释应该通过javadoc或者其他存档工具存档。这只能应用于注释

Inherited

从超类继承注释,而不是接口。作用于此注释的策略是RUNTIME,它只能应用于注释

Retention

表示作用于程序元素上的注释应该多长时间有效。请参见前面讨论的RetentionPolicy。作用于此注释的策略是RUNTIME,它只能应用于注释

Deprecated

标记一个不赞成使用的程序元素,告诉开发人员他们不应该再使用该元素。保持策略是SOURCE

Overrides

表示一个方法将要覆盖一个父类中的方法。如果确实不存在覆盖,那么编译器将会生成一个错误消息。这只能应用于方法

JDK 5引入了两个有用的源代码级别注释,即@deprecated和@overrides。@deprecated注释用来标记一个不赞成使用的方法,也就是说,客户端程序员不应该使用它。当在一个类方法中遇到程序员使用这种注释时,编译器将会发出一个警告。另一个注释@overrides,用来标记一个覆盖父类中方法的方法。编译器将会确认标记为@overrides的方法真正是覆盖了父类中的一个方法。如果子类中的方法没有覆盖父类中的方法,那么编译器将会发送一个错误,警告程序员方法签名不匹配父类中的方法。

开发一个自定义注释并不困难。下面创建一个CodeTag注释,它可以存储基本的作者信息和修改日期信息,同时也存储任意应用到该段代码的bug修复。注释将被限制到类和方法上:

import java.lang.annotation.*;

@Retention(RetentionPolicy. SOURCE)

@Target({ElementType. TYPE, ElementType.METHOD})

public @interface CodeTag {

     String authorName();

     String lastModificationDate();

     String bugFixes() default "";

}

Retention被设置为SOURCE,这意味着此注释在编译时和运行时是无效的。doclet API用来访问源代码级别上的注释。Target(对于类/接口/枚举)被设置为TYPE,对于方法则被设置为METHOD。如果CodeTag注释被应用到任意其他的源代码元素上,那么就会生成一个编译器错误。前面的两个注释元素是authorName和lastModificationDate,两者都是必要的。bugFixes元素如果没有指定,那么默认是空字符串。下面是一个使用CodeTag注释的示例类:

import java.lang.annotation.*;

@CodeTag(authorName="Dilbert",

           lastModificationDate="May 7,  2006")

public class ServerCommandProcessor {

     @CodeTag(authorName="Dilbert",

                lastModificationDate="May 10,  2006",

                bugFixes="BUG0170")

     public void setParams(String serverName)

     {

          //…

     }

     public void executeCommand(String command,  Object... params)

     {

          //…

     }

}

注意注释是如何用来标记谁对源代码进行了修改以及是何时修改的。由于bug的修复从而使得方法在类修改之后一天被修改。自定义的注释可以用来追踪关于源代码修改的信息。为了查看或者处理这些源代码注释,就必须使用doclet API。

doclet API(也叫做Javadoc API)已经扩展到可以支持源代码中的注释处理。要使用doclet API,就必须在类路径中包括tools.jar文件(对于版本5或更高的版本,位于默认JDK安装的库文件目录中)。通过编写一个扩展了com.sun.javadoc.Doclet的Java类就可以使用doclet API。必须实现start方法,因为这是Javadoc调用doclet以执行自定义处理的方法。下面这个简单的doclet打印一个Java源文件中的所有类和方法:

import com.sun.javadoc.*;

public class ListClasses extends Doclet {

     public static boolean start(RootDoc root)  {

          ClassDoc[] classes = root.classes();

           for  (ClassDoc cd :  classes)  {

                 System.out.println("Class  [" + cd + "]  has the following methods");

                 for(MemberDoc md :  cd.methods())  {

                     System.out.println(" "+ md);

                 }

           }

           return true;

     }

}

start方法将RootDoc作为一个参数,该参数通过javadoc工具被自动传入。从RootDoc开始可以访问源代码中的所有元素,以及关于命令行(例如增加的包和类)的信息。

针对注释添加到doclet API中的接口是AnnotationDesc、AnnotationDesc.Element ValuePair、AnnotationTypeDoc、AnnotationTypeElementDoc和AnnotationValue。

Java源代码中可以有注释的任意元素都具有一个annotations()方法,该方法与doclet API同源代码元素的对应部分相关联。这些元素是AnnotationTypeDoc、AnnotationTypeElementDoc、ClassDoc、ConstructorDoc、ExecutableMemberDoc、FieldDoc、MethodDoc以及MemberDoc。Annotations()方法返回一个AnnotationDesc数组。

1.AnnotationDesc

此类代表一个注释,它包括一个注释类型(AnnotationTypeDoc)和一个同它们的值相配对的注释类型元素数组。AnnotationDesc定义了表1-5中的方法。

表1-5

 

方法

描述

AnnotationTypeDoc annotationType()

返回这个注释的类型

AnnotationDesc.ElementValuePair[] elementValues()

返回一个注释元素的数组以及它们的值。只返回被显式列出的元素。没有被显式列出的元素(已经假设了它们的默认值),将不会被返回,因为此方法只处理被列出的元素。如果没有元素,那么将返回一个空数组

2.AnnotationDesc.ElementValuePair

它代表了一个注释类型的元素及其值之间的关联。它定义了表1-6中的方法。

表1-6

 

方法

描述

AnnotationTypeElementDoc element()

返回注释类型元素

AnnotationValue value()

返回注释类型元素的值

3.AnnotationTypeDoc

此接口代表了源代码中的一个注释,就如同ClassDoc代表一个Class一样。它只定义了一个方法,见表1-7。

表1-7

 

方法

描述

AnnotationTypeElementDoc[] elements()

返回此注释类型的元素的数组

4.AnnotationTypeElementDoc

此接口代表了一个注释类型的元素,它定了一个方法,见表1-8。

表1-8

 

方法

描述

AnnotationValue defaultValue()

返回与此注释类型相关联的默认值;如果没有默认值,则返回null

5.AnnotationValue

此接口代表了一个注释类型元素的值,它定义的方法见表1-9。

表1-9

 

方法

描述

String toString()

返回该值的一个字符串表示形式

Object value()

返回值。此值所表示的对象可以是下面的任意类型:
q      基本类型(例如Integer或者Float)的一个包装器类
q      一个String
q   一个Type(代表一个类、一个泛型类、一个类型变量、一个通配符类,或者一个基本数据类型)
q      一个FieldDoc(代表一个enum常量)
q      一个AnnotationDesc
q   一个AnnotationValue数组

下面是一个使用doclet API所提供的注释支持的示例。此doclet显示它在一个源文件中发现的所有注释及其值:

import com.sun.javadoc.*;

import java.lang.annotation.*;

public class AnnotationViewer {

     public static boolean start(RootDoc root)

     {

          ClassDoc[] classes = root.classes();

          for (ClassDoc cls : classes)  {

               showAnnotations(cls);

          }

          return(true);

     }

     static void showAnnotations(ClassDoc cls)

     {

          System.out.println("Annotations for class [" + cls + "]");

          process(cls.annotations());

          System.out.println();

          for(MethodDoc m : cls.methods())  {

               System.out.println("Annotations for method [" + m + "]");

               process(m.annotations());

               System.out.println();

         }

    }

    static void process(AnnotationDesc[] anns)

    {

         for (AnnotationDesc ad :anns)  {

               AnnotationDesc.ElementValuePair evp[]  = ad.elementValues();

               for(AnnotationDesc.ElementValuePair e : evp)  {

                    System.out.println("   NAME:  " + e.element()  +

                                           ",  VALUE="  + e.value()) ;

               }

          }

    }

}

Start方法迭代了在源文件中发现的所有类(和接口)。因为在源代码元素上的所有注释都是与AnnotationDesc接口相关联的,所以可以编写一个方法来处理注释,而不用考虑该注释关联的是哪个代码元素。showAnnotations方法打印出同当前类相关联的所有注释,然后处理该类中的所有方法。doclet API使得处理这些源代码元素很容易。为了执行doclet,可以按照如下方式向命令行的程序传递doclet名称和类名称:

javadoc -doclet AnnotationViewer ServerCommandProcessor.java

doclet在屏幕上显示如下内容:

Loading source file ServerCommandProcessor.java...

Constructing Javadoc information...

Annotations for class  [ServerCommandProcessor]

   NAME: CodeTag.authorName(), VALUE="Dilbert"

   NAME: CodeTag.lastModificationDate(), VALUE="May 7,  2006"

Annotations for method [ServerCommandProcessor.setParams(java fang. String)]

   NAME: CodeTag.authorName(), VALUE="Dilbert"

   NAME: CodeTag.lastModificationDate(), VALUE="May 10, 2006"

   NAME: CodeTag.bugFixes(), VALUE="BUG0170"

Annotations for method [ServerCommandProcessor.executeCommand (java.lang. Strinq,

java.lang.Object[])]

要在运行时访问注释,就必须使用反射API。通过AnnotatedElement接口已经内置了这种支持,该接口是通过反射类AccessibleObject、Class、Constructor、Field、Method和Package来实现的。所有这些元素都可以有注释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值