注解你真的深入了解了吗?(1)

注释,J2SE 5.0 (Tiger) 中的新功能,将非常需要的元数据工具引入核心 Java 语言。该系列文章分为两部分,在这第 1 部分中,作者 Brett McLaughlin 解释了元数据如此有用的原因,向您介绍了 Java 语言中的注释,并研究了 Tiger 的内置注释。

编程的一个最新的趋势,尤其是在 Java 编程方面,是使用 元数据。简单地说,元数据就是 关于数据的数据。元数据可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。许多元数据工具,如 XDoclet(请参阅 参考资料),将这些功能添加到核心 Java 语言中,暂时成为 Java 编程功能的一部分。

  直到可以使用 J2SE 5.0(也叫做 Tiger,现在是第二个 beta 版本),核心 Java 语言才最接近具有 Javadoc 方法的元数据工具。您使用特殊的标签集合来标记代码,并执行 javadoc 命令来将这些标签转化成格式化的 HTML 页面,该页面说明标签所附加到的类。然而,Javadoc 是有缺陷的元数据工具,因为除了生成文档之外,您没有固定、实用、标准化的方式来将数据用于其他用途。HTML 代码经常混入到 Javadoc 输出中这一事实甚至更进一步降低了其用于任何其他目的的价值。

  Tiger 通过名为 注释的新功能将一个更通用的元数据工具合并到核心 Java 语言中。注释是可以添加到代码中的修饰符,可以用于包声明、类型声明、构造函数、方法、字段、参数和变量。Tiger 包含内置注释,还支持您自己编写的定制注释。本文将概述元数据的优点并向您介绍 Tiger 的内置注释。本系列文章的 2 部分将研究定制注释。我要感谢 O'Reilly Media, Inc.,他们非常慷慨地 允许我在本文中使用我关于 Tiger 的书籍的注释一章中的代码示例(请参阅 参考资料)。

元数据的价值
一般来说,元数据的好处分为三类:文档编制、编译器检查和代码分析。代码级文档最

常被引用。元数据提供了一种有用的方法来指明方法是否取决于其他方法,它们是否完整,

特定类是否必须引用其他类,等等。这确实非常有用,但对于将元数据添加到 Java 语言中来说,文档编制可能是 最不相关的理由。Javadoc 已经提供了非常容易理解和健壮的方法来文档化代码。另外,当已经存在文档编制工具,并且在大多数时候都工作得很好时,谁还要编写文档编制工具?

编译器检查

  元数据更重要的优点是编译器可以使用它来执行基本的编译时检查。例如,您将在本文后面的 Override 注释中看到 Tiger 引入了一个这样的注释,用于允许您指定一种方法覆盖超类中的另一种方法。Java 编译器可以确保在元数据中指明的行为实际发生在代码级别。如果从来没有找出过这种类型的 bug,这样做似乎有点傻,但是大多数年龄很大的 Java 编程老手都曾经花费至少多个晚上来查明他们的代码为什么不能用。当最后认识到方法的参数有错,且该方法实际上 没有 覆盖超类中的方法时,您可能更感到难受。使用元数据的工具有助于轻松地查明这种类型的错误,从而可以节省那些晚上来看长期进行的 Halo 联赛。

代码分析

  可以证明,任何好的注释或元数据工具的最好功能就是可以使用额外数据来分析代码。在一个简单的案例中,您可能构建代码目录,提供必需的输入类型并指明返回类型。但是,您可能想,Java 反射具有相同的优点;毕竟,可以为所有这些信息内省代码。这从表面上看似乎是正确的,但是在实际中通常不使用。许多时候,方法作为输入接受的或者作为输出返回的类型实际上不是该方法想要的类型。例如,参数类型可能是 Object ,但方法可能仅使用 Integer 。这在好些情况下很容易发生,比如在方法被覆盖而超类使用常规参数声明方法时,还有正在进行许多序列化的系统中也容易发生。在这两种情况中,元数据可以指示代码分析工具,虽然参数类型是 Object ,但 Integer 才是真正需要的。这类分析非常有用,但也不能夸大它的价值。

  在更复杂的情况下,代码分析工具可以执行所有种类的额外任务。示例 du jour Enterprise JavaBean (EJB) 组件。甚至简单 EJB 系统中的依赖性和复杂性都非常令人吃惊。您具有了 home 接口和远程接口,以及本地接口和本地 home 接口,还有一个实现类。保持所有这些类同步非常困难。但是,元数据可以提供这个问题的解决放案。好的工具(还是要提一下 XDoclet)可以管理所有这些依赖性,并确保无代码级连接、但有逻辑级捆绑的类保持同步。元数据在这里确实可以发挥它的作用。

注释的基本知识

现在已经了解了元数据的好处,我将介绍 Tiger 中的注释。注释采用“at”标记形式 ( @ ),后面是注释名称。然后在需要数据时,通过 name=value 对向注释提供数据。每次使用这类表示法时,就是在生成注释。一段代码可能会有 10 个、50 个或更多的注释。不过,您将发现多个注释都可能使用相同的 注释类型。类型是实际使用的结构,在特定上下文中,注释本身是该类型的具体使用(请参阅侧栏 注释或注释类型?)。

注释分为三个基本种类:

  标记注释没有变量。注释显示简单,由名称标识,没有提供其他数据。例如, @MarkerAnnotation 是标记注释。它不包含数据,仅有注释名称。

  单一值注释与标记注释类似,但提供一段数据。因为仅提供很少的一点数据,所以可以使用快捷语法(假设注释类型接受此语法): @SingleValueAnnotation("my data") 。除了 @ 标记外,这应该与普通的 Java 方法调用很像。

  完整注释有多个数据成员。因此,必须使用更完整的语法(注释不再像普通的 Java 方法):@FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3")

  除了通过默认语法向注释提供值外,还可以在需要传送多个值时使用名称-值对。还可以通过花括号为注释变量提供值数组。清单 1 显示了注释中的值数组的示例。

  清单 1. 在注释中使用按数组排列的值

package com.easyway.commons.ispace.dev.advances.datameta;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

/**

 * 自定义注解

 * @author longgangbai

 * @date 2010-5-21

 * @version 1.0

 * @since JDK6.0

 */

@Target(ElementType.METHOD) //仅仅使用于方法

@Retention(RetentionPolicy.RUNTIME) //注解咋运行时,编译执行

public @interface TODO{

    TODOType severity() default TODOType.LOW;

    String item();

    String assignedTo();

    enum TODOType{

       CRITICAL,IMPOTANT,LOW

    }

}

package com.easyway.commons.ispace.dev.advances.datameta;

 

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

/**

 * 注解时的数组

 * @author longgangbai

 * @date 2010-5-21

 * @version 1.0

 * @since JDK6.0

 */

@Target(ElementType.TYPE)

@Retention(RetentionPolicy.RUNTIME) //注解咋运行时,编译执行

public @interface TODOItems {

    TODO[] value();

}

package com.easyway.commons.ispace.dev.advances.datameta;

 

import com.easyway.commons.ispace.dev.advances.datameta.TODO.TODOType;

/**

 * 自定义注解

 * @author longgangbai

 * @date 2010-5-21

 * @version 1.0

 * @since JDK6.0

 */

@TODOItems({// Curly braces indicate an array of values is being supplied

      @TODO(

       severity=TODOType.CRITICAL,

       item="Add functionality to calculate the mean of the student's grades",

       assignedTo="Brett McLaughlin"

       ),

       @TODO(

       severity=TODOType.IMPOTANT,

       item="Print usage message to screen if no command-line flags specified",

       assignedTo="Brett McLaughlin"

       ),

       @TODO(

       severity=TODOType.LOW,

       item="Roll a new website page with this class's new features",

       assignedTo="Jason Hunter"

       )

})

public class CustomerMeta {

}

清单 1 中的示例并没有乍一看那样复杂。 TODOItems 注释类型有一个具有值的变量。这里提供的值比较复杂,但 TODOItems 的使用实际与单一值注释类型相符,只是这里的单一值是数组而已。该数组包含三个 TODO 注释,其中每个注释都是多值的。逗号分隔每个注释内的值,以及单个数组内的值。非常容易,是吧?

  但是我讲的可能超前了些。 TODOItems TODO 定制注释,是本系列文章第 2 部分中的主题。但是我想让您看到,即使复杂注释(清单 1 几乎是最复杂的注释)也不是非常令人害怕的。当提到 Java 语言的标准注释类型时,将很少看到如此复杂的情况。正如将在下面三个部分了解到的,Tiger 的基本注释类型的使用都极其简单。

  

你可能感兴趣的:(数据结构,编程,J2SE,ejb)