跳至主要內容

反射(一)

guodongAndroid大约 7 分钟

前言

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