反射(一)
前言
在 上一篇文章 中我们简单学习了下泛型知识,那么本文主要学习下反射相关的知识。在 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 打下知识储备。
