反射(一)
前言
在 上一篇文章 中我们简单学习了下泛型知识,那么本文主要学习下反射相关的知识。在 Retrofit
中与反射相关的主要是 Method
,所以本文学习反射的重点在于 Method
相关的知识点。
在学习 Method
之前,我们先学习下 Type
相关的内容。
Type
package java.lang.reflect;
public interface Type {
default String getTypeName() {
return toString();
}
}
Type
类是 Java 反射中重要的组成部分。根据其注释描述:Type
是 Java 中所有类型的通用超接口。这些包括原始类型、参数化类型、数组类型、类型变量和基本类型。
下图是与 Type
相关的类图:
上图中 Type
的子类与 Type
注释中的描述基本一致:
- 原始类型、基本类型对应
Class
,表示 Java 类、数组,8种基本类型, - 数组类型对应
GenericArrayType
,表示参数化类型或者类型变量的数组,比如:List<String>[]
和T[]
等, - 参数化类型对应
ParameterizedType
,表示参数化类型的类,比如:Class<T>
- 类型变量对应
TypeVariable
,一般表示泛型变量, - 通配符类型对应
WildcardType
,表示泛型通配符,其没有在Type
注释中描述。
Class
Class
的确实现了 Type
接口:
public final class Class<T> implements Type {
}
有多种方式可以获取 Class
,比如以下几种方式:
Class<Integer> integerClass = int.class;
Class<? extends Integer> numberClass = number.getClass();
Class<?> strClass = Class.forName("java.lang.String");
Class<? extends Integer> numberClass = number.getClass();
System.out.println(numberClass.getTypeName());
上面的代码调用 Type#getTypeName()
方法,输出内容如下:java.lang.Integer
。
GenericArrayType
GenericArrayType
表示参数化类型或者类型变量的数组,比如:List<String>[]
和 T[]
等。
public interface GenericArrayType extends Type {
Type getGenericComponentType();
}
GenericArrayType
中仅有一个 getGenericComponentType
方法,方法返回数据元素的类型,比如:List<String>[]
返回 List<String>
。
下面是测试代码:
public class Test {
public <T> void testGenericArrayType(int[] ia, String[] sa, List<String>[] lsa, T[] ta) {
}
public static void main(String[] args) throws Exception {
Method method = Test.class.getMethod("testGenericArrayType", int[].class, String[].class, List[].class, Object[].class);
Type[] types = method.getGenericParameterTypes();
for (Type type : types) {
System.out.println(type.getTypeName());
System.out.println(type.getClass().getName());
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
System.out.println(componentType.getTypeName());
System.out.println(componentType.getClass().getName());
}
System.out.println("============================");
}
}
}
在上面代码中我们定义了一个 testGenericArrayType
方法,方法有四个参数,参数都是数组类型,接下来我们在 main
方法中通过反射获取 Method
,再通过getGenericParameterTypes
获取方法的参数类型数组,我们遍历方法参数类型数组,输出类型名称和类名称,并判断是否是 GenericArrayType
,如果是 GenericArrayType
类型再通过 getGenericComponentType
方法获取它的 componentType
,最后输出 componentType
的类型名称和类名称。
以上代码输出结果如下:
int[]
java.lang.Class
============================
java.lang.String[]
java.lang.Class
============================
java.util.List<java.lang.String>[]
sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
java.util.List<java.lang.String>
============================
T[]
sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
T
============================
通过上面的输出结果可以看:
int[]
和String[]
不是GenericArrayType
类型,而是Class
类型,因为他俩不属于参数化类型和类型变量,- 后面的两个参数都是
GenericArrayType
类型,而getGenericComponentType
方法获取的是[]
前的类型,如果是多维数组,那么它获取的是最后一个[]
之前的类型,比如:List<String>[][][]
,调用getGenericComponentType
方法返回List<String>[][]
。
我们修改下 testGenericArrayType
方法:List<String>[]
修改为 List<String>[][][]
:
public <T> void testGenericArrayType(int[] ia, String[] sa, List<String>[][][] lsa, T[] ta);
输出结果如下:
java.util.List<java.lang.String>[][][]
sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
java.util.List<java.lang.String>[][]
sun.reflect.generics.reflectiveObjects.GenericArrayTypeImpl
ParameterizedType
ParameterizedType
表示参数化类型的类,比如:Class<T>
等。
public interface ParameterizedType extends Type {
Type[] getActualTypeArguments();
Type getRawType();
Type getOwnerType();
}
ParameterizedType
接口有三个方法:
getActualTypeArguments
方法:参数化类型中的参数实际类型,比如:Class<String>
中的String
,getRawType
方法:参数化类型中的原始类型,比如:Class<String>
中的Class
,getOwnerType
方法:嵌套参数化类型中的上层类型,根据方法注释描述:对于O<T>.I<S>
中的I<S>
来说,它的ownerType
是O<T>
,而O<T>
是顶层类型,它没有ownerType
,返回 null。
下面我们看看测试代码:
package com.guodong.android.retrofit;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
public <T> void testParameterizedType(Class<T> clz) {
}
public static void main(String[] args) throws Exception {
Method method = Test.class.getMethod("testParameterizedType", Class.class);
Type[] types = method.getGenericParameterTypes();
for (Type type : types) {
System.out.println(type.getTypeName());
System.out.println(type.getClass().getName());
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
Type ownerType = parameterizedType.getOwnerType();
Type rawType = parameterizedType.getRawType();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println("ownerType = " + ownerType);
System.out.println("rawType = " + rawType);
System.out.println("actualTypeArguments = " + Arrays.toString(actualTypeArguments));
}
System.out.println("============================");
}
}
}
在上面的代码中,我们定义了一个 testParameterizedType
方法,它仅有一个 Class<T>
类型的参数,接下来我们在 main
方法中通过反射获取 Method
,再通过getGenericParameterTypes
获取方法的参数类型数组,我们遍历方法参数类型数组,输出类型名称和类名称,并判断是否是 ParameterizedType
,如果是 ParameterizedType
类型,再获取它的 ownerType
、rawType
和 actualTypeArguments
,并输出。
上述代码输出结果如下:
java.lang.Class<T>
sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
ownerType = null
rawType = class java.lang.Class
actualTypeArguments = [T]
============================
通过上面的输出结果可以看出:
- 对于
Class<T>
来说,它是顶层类型,它没有ownerType
,所以ownerType
为 null, - 它的原始类型
rawType
是java.lang.Class
, - 它的参数实际类型是:
T
。
TypeVariable
TypeVariable
一般表示泛型变量,在 Java 中有三个地方可以声明类型变量:
- Class,比如:
User<T>
, - Constructor,比如:
public <S> User(S s) {}
, - Method,比如:
public <F, R> R convert(F f) {}
。
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
Type[] getBounds();
D getGenericDeclaration();
String getName();
AnnotatedType[] getAnnotatedBounds();
}
TypeVariable
接口有四个方法:
getBounds
方法:获取泛型变量的上限,可以使用&
符号指定多个上限,所以此方法返回的是类型数组,如果没有显式指定上限,默认上限为Object
,getGenericDeclaration
方法:获取声明泛型变量的类型:是 Class 还是 Constructor,或者是 Method,getName
方法:获取声明泛型变量时的变量名称,如:Class<T>
中T
,getAnnotatedBounds
方法:此方法是 Java 1.8 添加的,获取声明泛型变量上限上的注解数组,数组内的顺序按注解声明顺序。
下面还是测试代码,我们就以 Class 为例吧:
首先声明 AnnotationTest
注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface AnnotationTest {
String value();
}
- 必须为
RUNTIME
运行时注解, - 必须作用在
TYPE_USE
元素上。
修改 Test
类:
public class Test<T extends @AnnotationTest("Closeable") Closeable & @AnnotationTest("Runnable") Runnable> {
public static void main(String[] args) throws Exception {
TypeVariable<Class<Test>>[] typeVariables = Test.class.getTypeParameters();
for (TypeVariable<Class<Test>> typeVariable : typeVariables) {
System.out.println("typeName = " + typeVariable.getTypeName());
System.out.println("name = " + typeVariable.getName());
System.out.println("genericDeclaration = " + typeVariable.getGenericDeclaration());
Type[] bounds = typeVariable.getBounds();
for (Type bound : bounds) {
System.out.println("boundTypeName = " + bound.getTypeName());
}
AnnotatedType[] annotatedBounds = typeVariable.getAnnotatedBounds();
for (AnnotatedType annotatedBound : annotatedBounds) {
System.out.println("annotateTypeName = " + annotatedBound.getType().getTypeName());
Annotation[] annotations = annotatedBound.getAnnotations();
for (Annotation declaredAnnotation : annotations) {
System.out.println("declaredAnnotation = " + declaredAnnotation.annotationType().getName());
}
}
System.out.println("=======================================");
}
}
}
在上面的代码中,我们为 Test
类定义了泛型变量 T
,同时指定它的上限为 Closeable
和 Runnbale
,并且为泛型变量上限加上了 AnnotationTest
注解,接下来我们在 main
方法中通过 Test.class.getTypeParameters();
获取 Test
的类型变量数组,遍历类型变量数组,依次输出 TypeName
、Name
、GenericDeclaration
、Bounds
和 AnnotatedBounds
。
上述代码输出结果如下:
typeName = T
name = T
genericDeclarationclass = com.guodong.android.retrofit.Test
boundTypeName = java.io.Closeable
boundTypeName = java.lang.Runnable
annotateTypeName = java.io.Closeable
declaredAnnotation = com.guodong.android.retrofit.AnnotationTest
annotateTypeName = java.lang.Runnable
declaredAnnotation = com.guodong.android.retrofit.AnnotationTest
=======================================
通过上面的输出结果可以看出:
- 类别变量的名称为:
T
, - 声明类型变量的类型为:
com.guodong.android.retrofit.Test
, - 类型变量有两个上限,分别为:
java.io.Closeable
和java.lang.Runnable
,且是按声明顺序排序, annotateTypeName
表示AnnotationTest
注解加的类型,declaredAnnotation
表示注解的全限定名。
WildcardType
WildcardType
表示泛型通配符。
public interface WildcardType extends Type {
Type[] getUpperBounds();
Type[] getLowerBounds();
}
WildcardType
接口有两个方法:
getUpperBounds
方法,获取泛型上限,如果没有显式指定上限,默认上限为Object
,getLowerBounds
方法,获取泛型下限,如果没有显式指定下限,默认下限为null
,此时长度为0
。
下面还是测试代码:
package com.guodong.android.retrofit;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Test {
private static Map<? super String, ? extends Number> map;
public static void main(String[] args) throws Exception {
Field field = Test.class.getDeclaredField("map");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
if (actualTypeArgument instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) actualTypeArgument;
Type[] upperBounds = wildcardType.getUpperBounds();
Type[] lowerBounds = wildcardType.getLowerBounds();
System.out.println("upperBounds = " + Arrays.toString(upperBounds));
System.out.println("lowerBounds = " + Arrays.toString(lowerBounds));
System.out.println("----------------------------------------------");
}
}
}
}
}
在上面的代码中,我们声明了一个 private static Map<? super String, ? extends Number> map;
静态变量,这个变量有两个泛型参数,第一个泛型参数指定了下限为 String
,第二个泛型参数指定了上限为 Number
,接下来我们在 main
方法中获取这个变量声明的泛型参数通配符并输出。
上述代码输出结果如下:
upperBounds = [class java.lang.Object]
lowerBounds = [class java.lang.String]
----------------------------------------------
upperBounds = [class java.lang.Number]
lowerBounds = []
----------------------------------------------
通过上面的输出结果可以看出:
- 第一个泛型参数没有指定上限,那么默认上限为
Object
, - 第一个泛型参数指定了下限
String
,实际输出也是String
, - 第二个泛型参数指定了上限
Number
,实际输出也是Number
, - 第二个泛型参数没有指定下限,那么默认下限为
null
,数组长度为0
,与getLowerBounds
方法注释描述相符。
总结
我们通过本章的学习,开启了反射知识的大门,为接下来我们学习 Retrofit
打下知识储备。