CH05-注解机制

概览

注解是 JDK 1.5 版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它主要的作用有以下四方面:

  • 生成文档,通过代码里标识的元数据生成javadoc文档。

  • 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。

  • 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。

  • 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例。

比如:

  • Java自带的标准注解,包括@Override@Deprecated@SuppressWarnings,分别用于标明重写某个方法、标明某个类或方法过时、标明要忽略的警告,用这些注解标明后编译器就会进行检查。
  • 元注解,元注解是用于定义注解的注解,包括@Retention@Target@Inherited@Documented@Retention用于标明注解被保留的阶段,@Target用于标明注解使用的范围,@Inherited用于标明注解可继承,@Documented用于标明是否生成javadoc文档。
  • 自定义注解,可以根据自己的需求定义注解,并可用元注解对自定义注解进行注解。

内置注解

  • @Override
  • @Deprecated
  • @SuppressWarnings

元注解

  • @Target
  • @Retention,@RetentionTarget
  • @Documented
  • @Inherited
  • @Repeatable
  • @Native

注解与反射接口

通过反射工具包 java.lang.reflect 中的 AnnotatedElement 可以访问注解信息。注意仅在注解被声明为 RUNTIME 时,才能在运行时获取注解信息,当 class 文件被加载时保存在 class 文件中的注解信息才会被 JVM 读取。

AnnotatedElement 接口是所有成语元素的父接口,所有程序通过反射获得了某个类的 AnnotatedElement 对象之后,就可以调用该对象的方法来访问具体的注解信息:

  • boolean isAnnotationPresent(Class<?extends Annotation> annotationClass)
  • <T extends Annotation> T getAnnotation(Class<T> annotationClass)
  • Annotation[] getAnnotations()
  • <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass)
  • <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass)
  • Annotation[] getDeclaredAnnotations()

自定义注解

定义注解:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyMethodAnnotation {

    public String title() default "";

    public String description() default "";

}

使用注解:

public class TestMethodAnnotation {

    @Override
    @MyMethodAnnotation(title = "toStringMethod", description = "override toString method")
    public String toString() {
        return "Override toString method";
    }

    @Deprecated
    @MyMethodAnnotation(title = "old static method", description = "deprecated old static method")
    public static void oldMethod() {
        System.out.println("old method, don't use it.");
    }

    @SuppressWarnings({"unchecked", "deprecation"})
    @MyMethodAnnotation(title = "test method", description = "suppress warning static method")
    public static void genericsTest() throws FileNotFoundException {
        List l = new ArrayList();
        l.add("abc");
        oldMethod();
    }
}

读取注解:

Method[] methods = TestMethodAnnotation.class.getClassLoader()
        .loadClass(("com.pdai.java.annotation.TestMethodAnnotation"))
        .getMethods();

for (Method method : methods) {
    // 方法上是否有MyMethodAnnotation注解
    if (method.isAnnotationPresent(MyMethodAnnotation.class)) {
        try {
            // 获取并遍历方法上的所有注解
            for (Annotation anno : method.getDeclaredAnnotations()) {
                System.out.println("Annotation in Method '"
                        + method + "' : " + anno);
            }

            // 获取MyMethodAnnotation对象信息
            MyMethodAnnotation methodAnno = method
                    .getAnnotation(MyMethodAnnotation.class);

            System.out.println(methodAnno.title());

        } catch (Throwable ex) {
            ex.printStackTrace();
        }
    }
}

注解特性

Java 8 新特性

  • @Repeatable
  • @ElementType.TYPE_USE
  • @ElementType.TYPE_PARAMETER

TYPE_USE 包括类型声明和类型参数声明,是为了方便设计者进行类型检查。

// 自定义ElementType.TYPE_PARAMETER注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
public @interface MyNotEmpty {
}

// 自定义ElementType.TYPE_USE注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface MyNotNull {
}

// 测试类
public class TypeParameterAndTypeUseAnnotation<@MyNotEmpty T>{

  //使用TYPE_PARAMETER类型,会编译不通过
//		public @MyNotEmpty T test(@MyNotEmpty T a){
//			new ArrayList<@MyNotEmpty String>();
//				return a;
//		}

  //使用TYPE_USE类型,编译通过
  public @MyNotNull T test2(@MyNotNull T a){
    new ArrayList<@MyNotNull String>();
    return a;
  }
}