Retrofit学习日记-反射(三)

119 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 7 天,点击查看活动详情

前言

上一篇文章 中我们学习了与反射相关的 Method 知识,而且在上篇文章最后说反射先学到这里。没想到这么快就食言了,本篇文章继续学习下与反射相关的知识,那么本文的重点就是 AnnotatedElement

概述

AnnotatedElement 类上的注释比较多,我们简单看下:AnnotatedElement 表示当前在此 VM 中运行的程序的带注解的元素。该接口允许以反射方式读取注解。此接口中方法返回的所有注解都是不可变的和可序列化的。该接口的方法返回的数组可以被调用者修改,而不影响返回给其他调用者的数组。注解可以以不同的形式存在于元素上,比如直接存在于元素上,或者以容器注解的形式间接存在于元素上等等。

存在类型

AnnotatedElement 类的注释中已经描述了注解的四种存在类型:

  1. 直接存在,
  2. 间接存在,
  3. 存在,
  4. 关联。

类注释中不仅描述了这四种存在类型,还描述了这四种存在类型的判断条件或者说满足条件,下面我们将一一学习。

直接存在

直接存在应该是我们经常遇见或者说经常使用的一种类型:就是某个元素直接被注解标注。

 public class Test {
 ​
     @Element
     public void test() {
     }
 }

在上述代码中,test() 方法被 @Element 注解直接标注了,此时 @Elment 注解直接存在于元素之上,这种类型称为直接存在

间接存在

间接存在目前(jdk 1.8)只有一种情况,那就是可重复注解:当我们使用可重复注解多次标注元素时,那么这个可重复注解就是间接存在于元素之上。

使用上一篇文章中的可重复注解:

 @Retention(RetentionPolicy.RUNTIME)
 public @interface Array {
     Element[] value() default {};
 }
 ​
 @Retention(RetentionPolicy.RUNTIME)
 @Repeatable(Array.class)
 public @interface Element {
 }
 ​
 public class Test {
 ​
     @Element
     @Element
     public void test() {
     }
 }

在上述代码中, test() 方法被 @Element 注解多次标注,此时 @Element 注解是间接存在于元素之上,因为编译完后 test() 方法实际上被 @Array 注解标记,这种类型称为间接存在

下面是 test() 方法的字节码:字节码中的 RuntimeVisibleAnnotations 表示方法运行时可见的注解,其中 #13#14#15 对应常量池中的编号,从常量池中我们也可以看到 #13#14#15 分别对应的内容。

 Constant pool:
 #13 = Utf8               Lcom/guodong/android/retrofit/Array;
 #14 = Utf8               value
 #15 = Utf8               Lcom/guodong/android/retrofit/Element;
 
 public com.guodong.android.retrofit.Test();
     descriptor: ()V
     flags: ACC_PUBLIC
     Code:
          0: return
       LineNumberTable:
         line 8: 0
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       1     0  this   Lcom/guodong/android/retrofit/Test;
     RuntimeVisibleAnnotations:
       0: #13(#14=[@#15(),@#15()])

存在

若是满足以下任一条件,则认为元素上存在注解:

  1. 元素上直接存在注解,
  2. 元素上没有直接存在的注解,并且元素是一个类,父类上存在可继承的注解。

简单点说:元素自身有注解或者元素父类上有可继承的注解。

创建可继承注解和父类:

 @Retention(RetentionPolicy.RUNTIME)
 @Inherited
 public @interface ParentAnnotation {
 }
 ​
 @ParentAnnotation
 public class TestParent {
 }

修改 Test 继承 TestParent

 public class Test extends TestParent {
 }

上述代码中 @ParentAnnotation 注解是可继承的,并且直接存在于 TestParent 类上,此时 Test 类继承了 TestParent 类,那么此时 @PatentAnnotation 存在Test 元素之上,这种类型称为存在

关联

若是满足以下任一条件,则认为元素上有关联注解:

  1. 元素上直接或间接存在注解,
  2. 元素上没有直接或间接存在的注解,并且元素是一个类,该注解与其父类有关联关系。

创建超类:

 @ParentAnnotation
 public class TestGrandfather {
 }

修改父类 TestParent,去掉父类上的 @ParentAnnotationTest 类不变:

 public class TestParent {
 }

上述代码中 @ParentAnnotaion 注解标注在 TestGradfather 类上,并且 Test 类间接继承了 TestGradfather,那么此时 @PatentAnnotationTest 是关联关系,这种类型称为关联

小结

AnnotatedElement 类的注释中描述了注解存在类型,那么它所提供的方法,也是基于注解存在类型来实现的,同时注释中也描述了它提供的方法与注解存在类型的关系,以下表格摘自 AnnotatedElement 类注释:

返回值方法直接存在间接存在存在关联
TgetAnnotation(Class)✔️
Annotation[]getAnnotations()✔️
T[]getAnnotationsByType(Class)✔️
TgetDeclaredAnnotation(Class)✔️
Annotation[]getDeclaredAnnotations()✔️
T[]getDeclaredAnnotationsByType(Class)✔️✔️

总结

AnnotatedElement 的学习可以让我们更好理解和运用注解,以及如何获取注解,同时也是对上一篇文章中 Method 获取注解的补充,其实 Method 间接实现了 AnnotatedElement 接口,AnnotatedElement 接口还有其他几个子类,后续我们再学习。

happy~