反射(三)
前言
在 上一篇文章 中我们学习了与反射相关的 Method
知识,而且在上篇文章最后说反射先学到这里。没想到这么快就食言了,本篇文章继续学习下与反射相关的知识,那么本文的重点就是 AnnotatedElement
。
概述
AnnotatedElement
类上的注释比较多,我们简单看下:AnnotatedElement
表示当前在此 VM 中运行的程序的带注解的元素。该接口允许以反射方式读取注解。此接口中方法返回的所有注解都是不可变的和可序列化的。该接口的方法返回的数组可以被调用者修改,而不影响返回给其他调用者的数组。注解可以以不同的形式存在于元素上,比如直接存在于元素上,或者以容器注解的形式间接存在于元素上等等。
存在类型
在 AnnotatedElement
类的注释中已经描述了注解的四种存在类型:
- 直接存在,
- 间接存在,
- 存在,
- 关联。
类注释中不仅描述了这四种存在类型,还描述了这四种存在类型的判断条件或者说满足条件,下面我们将一一学习。
直接存在
直接存在应该是我们经常遇见或者说经常使用的一种类型:就是某个元素直接被注解标注。
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()])
存在
若是满足以下任一条件,则认为元素上存在注解:
- 元素上直接存在注解,
- 元素上没有直接存在的注解,并且元素是一个类,父类上存在可继承的注解。
简单点说:元素自身有注解或者元素父类上有可继承的注解。
创建可继承注解和父类:
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ParentAnnotation {
}
@ParentAnnotation
public class TestParent {
}
修改 Test
继承 TestParent
:
public class Test extends TestParent {
}
上述代码中 @ParentAnnotation
注解是可继承的,并且直接存在于 TestParent
类上,此时 Test
类继承了 TestParent
类,那么此时 @PatentAnnotation
存在于 Test
元素之上,这种类型称为存在。
关联
若是满足以下任一条件,则认为元素上有关联注解:
- 元素上直接或间接存在注解,
- 元素上没有直接或间接存在的注解,并且元素是一个类,该注解与其父类有关联关系。
创建超类:
@ParentAnnotation
public class TestGrandfather {
}
修改父类 TestParent
,去掉父类上的 @ParentAnnotation
,Test
类不变:
public class TestParent extends TestGrandfather {
}
上述代码中 @ParentAnnotaion
注解标注在 TestGradfather
类上,并且 Test
类间接继承了 TestGradfather
,那么此时 @PatentAnnotation
与 Test
是关联关系,这种类型称为关联。
小结
AnnotatedElement
类的注释中描述了注解存在类型,那么它所提供的方法,也是基于注解存在类型来实现的,同时注释中也描述了它提供的方法与注解存在类型的关系,以下表格摘自 AnnotatedElement
类注释:
返回值 | 方法 | 直接存在 | 间接存在 | 存在 | 关联 |
---|---|---|---|---|---|
T | getAnnotation(Class<T>) | ✔️ | |||
Annotation[] | getAnnotations() | ✔️ | |||
T[] | getAnnotationsByType(Class<T>) | ✔️ | |||
T | getDeclaredAnnotation(Class<T>) | ✔️ | |||
Annotation[] | getDeclaredAnnotations() | ✔️ | |||
T[] | getDeclaredAnnotationsByType(Class<T>) | ✔️ | ✔️ |
总结
对 AnnotatedElement
的学习可以让我们更好理解和运用注解,以及如何获取注解,同时也是对上一篇文章中 Method
获取注解的补充,其实 Method
间接实现了 AnnotatedElement
接口,AnnotatedElement
接口还有其他几个子类,后续我们再学习。
happy~