Retrofit学习日记-反射(一)

115 阅读7分钟

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

前言

上一篇文章 中我们简单学习了下泛型知识,那么本文主要学习下反射相关的知识。在 Retrofit 中与反射相关的主要是 Method,所以本文学习反射的重点在于 Method 相关的知识点。

在学习 Method 之前,我们先学习下 Type 相关的内容。

Type

 package java.lang.reflect;
 ​
 public interface Type {
     
     default String getTypeName() {
         return toString();
     }
 }

Type 类是 Java 反射中重要的组成部分。根据其注释描述:Type是 Java 中所有类型的通用超接口。这些包括原始类型、参数化类型、数组类型、类型变量和基本类型。

下图是与 Type 相关的类图:

4.type.jpg

上图中 Type 的子类与 Type 注释中的描述基本一致:

  1. 原始类型、基本类型对应 Class,表示 Java 类、数组,8种基本类型,
  2. 数组类型对应 GenericArrayType,表示参数化类型或者类型变量的数组,比如:List<String>[]T[] 等,
  3. 参数化类型对应 ParameterizedType,表示参数化类型的类,比如:Class<T>
  4. 类型变量对应 TypeVariable,一般表示泛型变量,
  5. 通配符类型对应 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
 ============================

通过上面的输出结果可以看:

  1. int[]String[] 不是 GenericArrayType 类型,而是 Class 类型,因为他俩不属于参数化类型和类型变量,
  2. 后面的两个参数都是 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 接口有三个方法:

  1. getActualTypeArguments 方法:参数化类型中的参数实际类型,比如:Class<String> 中的 String
  2. getRawType 方法:参数化类型中的原始类型,比如:Class<String> 中的 Class
  3. getOwnerType 方法:嵌套参数化类型中的上层类型,根据方法注释描述:对于 O<T>.I<S> 中的 I<S> 来说,它的 ownerTypeO<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 类型,再获取它的 ownerTyperawTypeactualTypeArguments,并输出。

上述代码输出结果如下:

 java.lang.Class<T>
 sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
 ownerType = null
 rawType = class java.lang.Class
 actualTypeArguments = [T]
 ============================

通过上面的输出结果可以看出:

  1. 对于 Class<T> 来说,它是顶层类型,它没有 ownerType,所以 ownerType 为 null,
  2. 它的原始类型 rawTypejava.lang.Class
  3. 它的参数实际类型是:T

TypeVariable

TypeVariable 一般表示泛型变量,在 Java 中有三个地方可以声明类型变量:

  1. Class,比如:User<T>
  2. Constructor,比如: public <S> User(S s) {}
  3. 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 接口有四个方法:

  1. getBounds 方法:获取泛型变量的上限,可以使用 & 符号指定多个上限,所以此方法返回的是类型数组,如果没有显式指定上限,默认上限为 Object
  2. getGenericDeclaration 方法:获取声明泛型变量的类型:是 Class 还是 Constructor,或者是 Method,
  3. getName 方法:获取声明泛型变量时的变量名称,如:Class<T>T
  4. getAnnotatedBounds 方法:此方法是 Java 1.8 添加的,获取声明泛型变量上限上的注解数组,数组内的顺序按注解声明顺序。

下面还是测试代码,我们就以 Class 为例吧:

首先声明 AnnotationTest 注解:

 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE_USE)
 public @interface AnnotationTest {
     String value();
 }
  1. 必须为 RUNTIME 运行时注解,
  2. 必须作用在 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,同时指定它的上限为 CloseableRunnbale ,并且为泛型变量上限加上了 AnnotationTest 注解,接下来我们在 main 方法中通过 Test.class.getTypeParameters(); 获取 Test 的类型变量数组,遍历类型变量数组,依次输出 TypeNameNameGenericDeclarationBoundsAnnotatedBounds

上述代码输出结果如下:

 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
 =======================================

通过上面的输出结果可以看出:

  1. 类别变量的名称为:T
  2. 声明类型变量的类型为:com.guodong.android.retrofit.Test
  3. 类型变量有两个上限,分别为:java.io.Closeablejava.lang.Runnable,且是按声明顺序排序,
  4. annotateTypeName 表示 AnnotationTest 注解加的类型,
  5. declaredAnnotation 表示注解的全限定名。

WildcardType

WildcardType 表示泛型通配符。

 public interface WildcardType extends Type {
     
     Type[] getUpperBounds();
     
     Type[] getLowerBounds();
 }

WildcardType 接口有两个方法:

  1. getUpperBounds 方法,获取泛型上限,如果没有显式指定上限,默认上限为 Object
  2. 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 = []
 ----------------------------------------------

通过上面的输出结果可以看出:

  1. 第一个泛型参数没有指定上限,那么默认上限为 Object
  2. 第一个泛型参数指定了下限 String,实际输出也是 String
  3. 第二个泛型参数指定了上限 Number,实际输出也是 Number
  4. 第二个泛型参数没有指定下限,那么默认下限为 null,数组长度为 0,与 getLowerBounds 方法注释描述相符。

总结

我们通过本章的学习,开启了反射知识的大门,为接下来我们学习 Retrofit 打下知识储备。