Java Annotation

what:

从 JDK 1.5 版本开始,Java 语言提供了通用的 Annotation 功能,允许开发者定义和使用自己的 Annotation 类型。Annotation 功能包括了关于定义 Annotation 类型的语法,对声明式进行注解的语法,读取 Annotation 的 API,Annotation 在 class 文件中的表现,以及 Annotation 处理工具(APT)。

Annotation 并不直接对程序的语法产生作用,但是会提供一些程序之外的数据或者信息,影响工具或者类库对程序的处理或者调用的方式,从而最终影响程序运行时的行为。

Annotation 功能现在已经广泛应用于各种基于 Java 的应用系统的开发。Java API 本身就预定义了一系列的 Annotation 类型,例如 @Deprecated, @Override, @SuppressWarnings 等等。

因此对于 Java 开发者来说,很可能已经通过使用 Annotation 来对代码和软件质量进行了提升。以 @Override 标签为例,在具有复杂继承结构的大型项目中,对于父类的某一个方法,开发者很难直观地通过代码判断出是哪一个子类的具体实现在运行时被调用。而一旦开发者改变了父类的方法名或其参数,而忽视了对子类作出同样的修改,子类中重写的方法将不会再被调用,从而导致系统无法呈现出预期的行为或结果。通过引入 @Override 标签,Java 编译器在对相关代码进行编译时会提示开发者对所有相关的子类中的重写方法进行同步修改,从而避免这一问题的产生。

为了更好地帮助开发者提升代码的质量和可读性,以及自动化代码分析的准确性,Java 8 对 Annotation 引入了两项重要的改变:Type Annotation 和 Repeating Annotation。

特性一:Type Annotation

在 Java 8 之前的版本中,只能允许在声明式前使用 Annotation。而在 Java 8 版本中,Annotation 可以被用在任何使用 Type 的地方,例如:初始化对象时 (new),对象类型转化时,使用 implements 表达式时,或者使用 throws 表达式时。

在 Java 8 之前的版本中,只能允许在声明式前使用 Annotation。而在 Java 8 版本中,Annotation 可以被用在任何使用 Type 的地方,例如:初始化对象时 (new),对象类型转化时,使用 implements 表达式时,或者使用 throws 表达式时。

清单 1. Type Annotation 使用示例

//初始化对象时
String myString = new @NotNull String();
//对象类型转化时
myString = (@NonNull String) str;
//使用 implements 表达式时
class MyList<T> implements @ReadOnly List<@ReadOnly T>{
                    ...
 }
 //使用 throws 表达式时
 public void validateValues() throws @Critical ValidationFailedException{
                    ...
  }

定义一个 Type Annotation 的方法与普通的 Annotation 类似,只需要指定 Target 为 ElementType.TYPE_PARAMETER 或者 ElementType.TYPE_USE,或者同时指定这两个 Target。

清单 2. 定义 Type Annotation 示例

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public  @interface MyAnnotation {
}

ElementType.TYPE_PARAMETER 表示这个 Annotation 可以用在 Type 的声明式前,而 ElementType.TYPE_USE 表示这个 Annotation 可以用在所有使用 Type 的地方(如:泛型,类型转换等)

与 Java 8 之前的 Annotation 类似的是,Type Annotation 也可以通过设置 Retention 在编译后保留在 class 文件中(RetentionPolicy.CLASS)或者运行时可访问(RetentionPolicy.RUNTIME)。但是与之前不同的是,Type Annotation 有两个新的特性:在本地变量上的 Annotation 可以保留在 class 文件中,以及泛型类型可以被保留甚至在运行时被访问。

虽然 Type Annotation 可以保留在 class 文件中,但是它并不会改变程序代码本身的行为。例如在一个方法前加上 Annotation,调用此方法返回的结果和不加 Annotation 的时候一致。

Java 8 通过引入 Type Annotation,使得开发者可以在更多的地方使用 Annotation,从而能够更全面地对代码进行分析以及进行更强的类型检查。

特性二:Repeating Annotation

在实际应用中,可能会出现需要对同一个声明式或者类型加上相同的 Annotation(包含不同的属性值)的情况。

例如系统中除了管理员之外,还添加了超级管理员这一权限,对于某些只能由这两种角色调用的特定方法,可以使用 Repeating Annotation。

清单 3. Repeating Annotation 使用示例-1

@Access(role="SuperAdministrator")
 @Access(role="Administrator")
 public void doCheck() { 
              ......
 }

上面的示例是针对方法使用 Annotation, 开发者也可以根据产品中的具体需求在其他地方使用 Repeating Annotation。例如某个类专门提供管理员相关的功能,可以直接在这个类上标注同样的 Annotation。

清单 4. Repeating Annotation 使用示例-2

@Access(role="SuperAdministrator")
@Access(role="Administrator")
public class AdminServices{
}

之前版本的 JDK 并不允许开发者在同一个声明式前加注同样的 Annotation,(即使属性值不同)这样的代码在编译过程中会提示错误。而 Java 8 解除了这一限制,开发者可以根据各自系统中的实际需求在所有可以使用 Annotation 的地方使用 Repeating Annotation。

由于兼容性的缘故,Repeating Annotation 并不是所有新定义的 Annotation 的默认特性,需要开发者根据自己的需求决定新定义的 Annotation 是否可以重复标注。Java 编译器会自动把 Repeating Annotation 储存到指定的 Container Annotation 中。而为了触发编译器进行这一操作,开发者需要进行以下的定义:

首先,在需要重复标注特性的 Annotation 前加上 @Repeatable 标签,示例如下:

清单 5. 定义 Repeating Annotation 示例

@Repeatable(AccessContainer.class)
public @interface Access {
        String role();
}

@Repeatable 标签后括号中的值即为指定的 Container Annotation 的类型。在这个例子中,Container Annotation 的类型是 AccessContainer,Java 编译器会把重复的 Access 对象保存在 AccessContainer 中。

AccessContainer 中必须定义返回数组类型的 value 方法。数组中元素的类型必须为对应的 Repeating Annotation 类型。具体示例如下:

清单 6. 定义 Container Annotation 示例

public @interface AccessContainer {
       Access[] value();
}

可以通过 Java 的反射机制获取注解的 Annotation。一种方式是通过 AnnotatedElement 接口的 getAnnotationByType(Class) 首先获得 Container Annotation,然后再通过 Container Annotation 的 value 方法获得 Repeating Annotation。另一种方式是用过 AnnotatedElement 接口的 getAnnotations(Class) 方法一次性返回 Repeating Annotation。

Repeating Annotation 使得开发者可以根据具体的需求对同一个声明式或者类型加上同一类型的注解,从而增加代码的灵活性和可读性。

使用场景

Type Annotation 为 Java 程序中的校验提供了更多的可能性,然而,相对于业务方面的校验,它更适用于计算机科学相关的一般性的格式校验。例如,空指针校验,数值范围校验,字符串格式校验等,都可以很方便地通过使用 Type Annotation 结合第三方的校验工具,如 Checker Framework 进行校验,避免程序在运行时出现此类错误或异常。

然而一些特定的业务逻辑方面的校验可能并不适合通过 Type Annotation 进行实现。例如在做转账交易时,需要检查用户权限以及账户余额等,这类的校验需要涉及各种不同的业务数据,很难通过一般性的校验框架执行校验。

结束语

在 Java 8 中,Annotation 得到了很好的扩展,可以用在任何使用 Type 的地方,而不仅限于声明式前。Annotation 本身并不会改变程序的运行行为,但是可以通过使用第三方工具,如 Checker Framework, 开发者可以利用 Annotation 去自动化地对程序中的潜在缺陷进行检测,提升软件质量,避免重复开发,提高开发效率。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值