天天看点

Java Type Annotation

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

//初始化对象时
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。

@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,从而能够更全面地对代码进行分析以及进行更强的类型检查。

TYPE_USE Annotation

@Target({ ElementType.FIELD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PrimeNumber1 {
    }

    @Target({ ElementType.TYPE_USE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PrimeNumber2 {
    }

    @Target({ ElementType.TYPE_USE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PrimeNumber3 {
    }      

定义如上三个注解,则在一个类中定义一个域时,可以有如下这种方式;

@PrimeNumber1
private @PrimeNumber2 String anInt = new @PrimeNumber3 String();      

其中在PrimeNumber1和PrimeNumber2 这两个位置是一样的效果,这个一样的效果是说,同一个注解放在PrimeNumber1处还是PrimeNumber2处,作用是一样的,用Java Reflect API的时候(Field.getAnnotations()或者Field.getAnnotatedType().getAnnotations()),注解放在这两个位置都能获取到;

Field.getAnnotations()是用来获取Target为ElementType.FIELD的注解;

Field.getAnnotatedType().getAnnotations()用来获取Target为ElementType.TYPE_USE的注解;

TYPE_PARAMETER Annotation

public class GetAnnotatedTypeExample2<@PrimeNumber4 T> {
  public static void main(String... args) throws NoSuchFieldException {
    TypeVariable<Class<GetAnnotatedTypeExample2>>[] tv = GetAnnotatedTypeExample2.class.getTypeParameters();
    System.out.println(tv[0].getAnnotations()[0].toString());
  }
}
@Target({ ElementType.TYPE_PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface PrimeNumber4 {
}      

类型注解的作用

类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework(注:此插件so easy,这里不介绍了),可以在编译的时候检测出runtime error(eg:UnsupportedOperationException; NumberFormatException;NullPointerException异常等都是runtime error),以提高代码质量。这就是类型注解的作用。

Note:

使用Checker Framework可以找到类型注解出现的地方并检查。

eg:

import checkers.nullness.quals.*; 
public class TestDemo{ 
void sample() { 
@NonNull Object my = new Object(); 
} 
}      

使用javac编译上面的类:(当然若下载了Checker Framework插件就不需要这么麻烦了)

javac -processor checkers.nullness.NullnessChecker TestDemo.java      

上面编译是通过的,但若修改代码:

@NonNull Object my = null;      

但若不想使用类型注解检测出来错误,则不需要processor,正常javac TestDemo.java是可以通过编译的,但是运行时会报 NullPointerException 异常。

为了能在编译期间就自动检查出这类异常,可以通过类型注解结合 Checker Framework 提前排查出来错误异常。

注意java 5,6,7版本是不支持注解@NonNull,但checker framework 有个向下兼容的解决方案,就是将类型注解@NonNull 用/**/注释起来。

import checkers.nullness.quals.*; 
public class TestDemo{ 
void sample() { 
/*@NonNull*/ Object my = null; 
} 
}