import java.lang.annotation.*; //使用Java8新增@Repeatable原注解 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(FilterPaths.class)//参数指明接收的注解class public @interface FilterPath { String value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface FilterPaths { FilterPath[] value(); } //使用案例 @FilterPath("/web/update") @FilterPath("/web/add") @FilterPath("/web/delete") class AA{ }
我们可以简单理解为通过使用@Repeatable后,将使用@FilterPaths注解作为接收同一个类型上重复注解的容器,而每个@FilterPath则负责保存指定的路径串。为了处理上述的新增注解,Java8还在AnnotatedElement接口新增了getDeclaredAnnotationsByType() 和 getAnnotationsByType()两个方法并在接口给出了默认实现,在指定@Repeatable的注解时,可以通过这两个方法获取到注解相关信息。但请注意,旧版API中的getDeclaredAnnotation()和 getAnnotation()是不对@Repeatable注解的处理的(除非该注解没有在同一个声明上重复出现)。注意getDeclaredAnnotationsByType方法获取到的注解不包括父类,其实当 getAnnotationsByType()方法调用时,其内部先执行了getDeclaredAnnotationsByType方法,只有当前类不存在指定注解时,getAnnotationsByType()才会继续从其父类寻找,但请注意如果@FilterPath和@FilterPaths没有使用了@Inherited的话,仍然无法获取。下面通过代码来演示:
//使用Java8新增@Repeatable原注解 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Repeatable(FilterPaths.class) public @interface FilterPath { String value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface FilterPaths { FilterPath[] value(); } @FilterPath("/web/list") class CC { } //使用案例 @FilterPath("/web/update") @FilterPath("/web/add") @FilterPath("/web/delete") class AA extends CC{ public static void main(String[] args) { Class<?> clazz = AA.class; //通过getAnnotationsByType方法获取所有重复注解 FilterPath[] annotationsByType = clazz.getAnnotationsByType(FilterPath.class); FilterPath[] annotationsByType2 = clazz.getDeclaredAnnotationsByType(FilterPath.class); if (annotationsByType != null) { for (FilterPath filter : annotationsByType) { System.out.println("1:"+filter.value()); } } System.out.println("-----------------"); if (annotationsByType2 != null) { for (FilterPath filter : annotationsByType2) { System.out.println("2:"+filter.value()); } } System.out.println("使用getAnnotation的结果:"+clazz.getAnnotation(FilterPath.class)); /** * 执行结果(当前类拥有该注解FilterPath,则不会从CC父类寻找) 1:/web/update 1:/web/add 1:/web/delete ----------------- 2:/web/update 2:/web/add 2:/web/delete 使用getAnnotation的结果:null */ } }
从执行结果来看如果当前类拥有该注解@FilterPath,则getAnnotationsByType方法不会从CC父类寻找,下面看看另外一种情况,即AA类上没有@FilterPath注解
//使用Java8新增@Repeatable原注解 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited //添加可继承元注解 @Repeatable(FilterPaths.class) public @interface FilterPath { String value(); } @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Inherited //添加可继承元注解 @interface FilterPaths { FilterPath[] value(); } @FilterPath("/web/list") @FilterPath("/web/getList") class CC { } //AA上不使用@FilterPath注解,getAnnotationsByType将会从父类查询 class AA extends CC{ public static void main(String[] args) { Class<?> clazz = AA.class; //通过getAnnotationsByType方法获取所有重复注解 FilterPath[] annotationsByType = clazz.getAnnotationsByType(FilterPath.class); FilterPath[] annotationsByType2 = clazz.getDeclaredAnnotationsByType(FilterPath.class); if (annotationsByType != null) { for (FilterPath filter : annotationsByType) { System.out.println("1:"+filter.value()); } } System.out.println("-----------------"); if (annotationsByType2 != null) { for (FilterPath filter : annotationsByType2) { System.out.println("2:"+filter.value()); } } System.out.println("使用getAnnotation的结果:"+clazz.getAnnotation(FilterPath.class)); /** * 执行结果(当前类没有@FilterPath,getAnnotationsByType方法从CC父类寻找) 1:/web/list 1:/web/getList ----------------- 使用getAnnotation的结果:null */ } }
注意定义@FilterPath和@FilterPath时必须指明@Inherited,getAnnotationsByType方法否则依旧无法从父类获取@FilterPath注解,这是为什么呢,不妨看看getAnnotationsByType方法的实现源码:
//接口默认实现方法 default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) { //先调用getDeclaredAnnotationsByType方法 T[] result = getDeclaredAnnotationsByType(annotationClass); //判断当前类获取到的注解数组是否为0 if (result.length == 0 && this instanceof Class && //判断定义注解上是否使用了@Inherited元注解 AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable //从父类获取 Class<?> superClass = ((Class<?>) this).getSuperclass(); if (superClass != null) { result = superClass.getAnnotationsByType(annotationClass); } } return result; }
2、新增的两种ElementType
在Java8中 ElementType 新增两个枚举成员,TYPE_PARAMETER 和 TYPE_USE ,在Java8前注解只能标注在一个声明(如字段、类、方法)上,Java8后,新增的TYPE_PARAMETER可以用于标注类型参数,而TYPE_USE则可以用于标注任意类型(不包括class)。如下所示
//TYPE_PARAMETER 标注在类型参数上 class D<@Parameter T> { } //TYPE_USE则可以用于标注任意类型(不包括class) //用于父类或者接口 class Image implements @Rectangular Shape { } //用于构造函数 new @Path String("/usr/bin") //用于强制转换和instanceof检查,注意这些注解中用于外部工具,它们不会对类型转换或者instanceof的检查行为带来任何影响。 String path=(@Path String)input; if(input instanceof @Path String) //用于指定异常 public Person read() throws @Localized IOException. //用于通配符绑定 List<@ReadOnly ? extends Person> List<? extends @ReadOnly Person> @NotNull String.class //非法,不能标注class import java.lang.@NotNull String //非法,不能标注import
这里主要说明一下TYPE_USE,类型注解用来支持在Java的程序中做强类型检查,配合第三方插件工具(如Checker Framework),可以在编译期检测出runtime error(如UnsupportedOperationException、NullPointerException异常),避免异常延续到运行期才发现,从而提高代码质量,这就是类型注解的主要作用。总之Java 8 新增加了两个注解的元素类型ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER ,通过它们,我们可以把注解应用到各种新场合中。
下一篇:源码级注解处理器!!!待更,敬请关注。