跳至主要內容

反射(三)

guodongAndroid大约 4 分钟

前言

上一篇文章 中我们学习了与反射相关的 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 extends TestGrandfather {
}

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

小结

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

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

总结

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

happy~