Java筑基——反射(2):泛型周边信息获取

相关文章:

1. 前言

本文主要介绍获取泛型超类和泛型接口的相关信息;Type 的分类以及在泛型超类和泛型接口中的使用案例。
这部分内容不是很好理解,需要好好消化消化。这部分内容算是反射里比较难懂的部分,有必要好好学习掌握,这样才能在阅读源码时更加顺畅。

2. 正文

2.1 获取泛型超类和泛型接口的相关信息

2.1.1 获取泛型超类的相关信息

在上篇文章 Java筑基——反射(1):基本类周边信息获取中,我们介绍了

// 获取 Class 对象的普通类型父类
public native Class<? super T> getSuperclass();

但是没有介绍:

// 获取 Class 对象的泛型类型父类
public Type getGenericSuperclass()

这里,我们会重点介绍这个方法的使用。

先介绍一下用于演示的类:

Holder 类,是一个泛型类,它有一个类型参数是 T

public class Holder<T> {
    public T t;

    public Holder(T t) {
        this.t = t;
    }
}

为了使 Holder 类看起来简洁,我特意省略了 getter/setter 方法。

IntegerHolder类, 是一个普通类,它继承于 Holder,并且给 Holder 的泛型类型参数传递了 Integer 这个实际类型:

public class IntegerHolder extends Holder<Integer>{
    public IntegerHolder(Integer integer) {
        super(integer);
    }
}

准备工作就绪,开始代码测试:

Type type = IntegerHolder.class.getGenericSuperclass();
System.out.println("type = " + type);

在这里,调用 IntegerHolder 对应的 Class 对象上的 getGenericSuperclass(),获取到的是一个 Type 类型的对象。并且打印了 Type 对象的信息。

查看打印结果:

type = com.java.advanced.features.reflect.genericsclass.blog.Holder<java.lang.Integer>

可以看到,通过 getGenericSuperclass() 获取到的 Type 对象里不仅仅包含了 IntegerHolder 的超类 Holder 的信息,还包括了实际类型参数 Integer

作为对比,getSuperclass() 方法只能获取到 IntegerHolder 的超类 Holder 而已。

Class<? super IntegerHolder> superclass = IntegerHolder.class.getSuperclass();
System.out.println("superclass = " + superclass);

打印结果:

superclass = class com.java.advanced.features.reflect.genericsclass.blog.Holder

到这里,有没有同学会想 Type 是什么?如何分别获取超类信息和实际类型参数的信息?

我们去查一下文档的说明:

可以看到 Type 是一个接口,从 1.5 开始引入的,它有 4 个子接口:GenericArrayTypeParameterizedTypeTypeVariableWildcardType;1 个实现类:Class

类图如下:

类图中列出了各个子接口以及子接口的方法,希望大家有一个整体的概念。但是,在这里,还不会一一去介绍。我们会在 2.2 中详细介绍 Type 的使用。

回到我们这个小节里,现在我们已经知道了 Type 是什么;接着看如何分别获取超类信息和实际类型参数的信息?

请看下面的测试代码如下:

public class _01_GetGenericSuperClassTest {
    public static void main(String[] args) {
        Type type = IntegerHolder.class.getGenericSuperclass();
        System.out.println("type = " + type);
        if (type instanceof ParameterizedType) {
        	// 把 Type 类型强制类型转换为 ParameterizedType 类型
            ParameterizedType parameterizedType = (ParameterizedType) type;
            // 获取替换了泛型类型参数的实际类型的 Type 对象的数组
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                Class typeArgument = (Class) actualTypeArgument;
                System.out.println("替换泛型类型参数的实际类型为:" + typeArgument.getName());
            }
            // 获取声明了泛型类型的原始类型
            Type rawType = parameterizedType.getRawType();
            Class rawClass = (Class) rawType;
            System.out.println("声明了泛型类型的原始类型为:" + rawClass.getName());
        }
    }
}

打印结果:

type = com.java.advanced.features.reflect.genericsclass.blog.Holder<java.lang.Integer>
替换泛型类型参数的实际类型为:java.lang.Integer
声明了泛型类型的原始类型为:com.java.advanced.features.reflect.genericsclass.blog.Holder

可以看到,代码中通过了 type instanceof ParameterizedType 判断,说明 typeParameterizedType 的一个实例,再看一下 type 的打印是 Holder<Integer>,它是一个泛型类型,对应的是 ParameterizedType

然后,我们通过调用 ParameterizedType 的两个方法:

Type[] getActualTypeArguments();
Type getRawType();

第一个方法是获取替换了泛型类型参数的实际类型的 Type对象的数组。这个方法里,我们需要注意的是,返回的是一个数组,而且数组的元素也是 Type 类型的。

这是为什么呢?返回一个数组是因为泛型类型参数可以有多个,就像一个方法可以声明多个形式参数一样。数组的元素类型是 Type,这是因为不仅仅是 Class 用于替换类型参数,其他的 Type 子接口也可以。

第二个方法是获取声明了泛型类型的原始类型。需要注意的一点是“原始类型"的概念一定是和泛型类型在一起的。如果不是泛型类型,那么就无所谓原始类型。

2.1.2 获取泛型接口的相关信息

在上篇文章 Java筑基——反射(1):基本类周边信息获取中,我们介绍了

// 获取 Class 对象的直接继承或实现的接口的 Class 对象列表
public Class<?>[] getInterfaces() 

但是没有介绍:

// 获取 Class 对象的直接继承或实现的泛型接口 Class 对象列表
public Type[] getGenericInterfaces()

本小节,我们会重点介绍这个方法的使用。

先介绍一下,参与测试的类:

Generator 是一个泛型接口,定义了一个生成器,定义一个 next() 方法:

public interface Generator<T> {
    T next();
}

IntegerGenerator 实现了 Generator 接口和 Serializable 接口,使用 Integer 替换泛型接口 Generator 的类型参数:

public class IntegerGenerator implements Generator<Integer>, Serializable {
    Random random = new Random();
    @Override
    public Integer next() {
        return random.nextInt();
    }
}

下面看测试代码:

Type[] types = IntegerGenerator.class.getGenericInterfaces();
for (Type type : types) {
    System.out.println("type = " + type);
}

打印结果:

type = com.java.advanced.features.reflect.genericsclass.blog.Generator<java.lang.Integer>
type = interface java.io.Serializable

可以看到,getGenericInterfaces() 不仅可以获取泛型类型的超类接口,还可以获取普通类型的超类接口。

和 2.1.1 中同样地,我们对获取到的 Type 对象进一步拆分出需要的信息。

测试代码如下:

public class _02_GetGenericInterfacesTest {
    public static void main(String[] args) {
        Type[] types = IntegerGenerator.class.getGenericInterfaces();
        for (Type type : types) {
            System.out.println("type = " + type);
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    Class typeArgument = (Class) actualTypeArgument;
                    System.out.println("替换泛型类型参数的实际类型为: " + typeArgument);
                }
                Type rawType = parameterizedType.getRawType();
                Class rawClass = (Class) rawType;
                System.out.println("声明了泛型接口的原始类型为: " + rawClass);
            }
        }
    }
}

打印信息:

type = com.java.advanced.features.reflect.genericsclass.blog.Generator<java.lang.Integer>
替换泛型类型参数的实际类型为: class java.lang.Integer
声明了泛型接口的原始类型为: interface com.java.advanced.features.reflect.genericsclass.blog.Generator
type = interface java.io.Serializable

2.2 Type

在 2.1 中,演示了 ParameterizedTypeClass 的使用,本节会演示 TypeVariableGenericArrayTypeWildcardType 的使用。

2.2.1 ParameterizedType

ParameterizedType 表示参数化类型,如 Collection<String>

2.2.2 TypeVariable

文档上的定义是,TypeVariable 是各种类型变量的公共高级接口。可是看了之后,还是不能明白。
我们可以这样理解:当获取到的 Type 对象表示是一个泛型的类型参数时,它就是 TypeVariable

下面我们仍是会通过代码演示。

首先,定义一个 NumberGenerator 泛型接口,它有一个泛型类型参数 T,限定为 Number 及其子类型;NumberGenerator 接口继承于 Generator 接口。

public interface NumberGenerator<T extends Number> extends Generator<T>{
}

测试代码如下:

public class _03_TypeVariableTest {
    public static void main(String[] args) {
        // 获取此接口直接继承的接口对应的 Type 对象数组
        Type[] types = NumberGenerator.class.getGenericInterfaces();
        for (Type type : types) {
            System.out.println("type = " + type); // 打印:Generator<T>
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                // 获取替换了泛型类型参数的实际类型的 Type 对象数组
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                   if( actualTypeArgument instanceof TypeVariable) {
                       TypeVariable typeVariable = (TypeVariable) actualTypeArgument;
                       System.out.println("泛型类型参数的名字为 " + typeVariable.getName()); // 打印:T
                       Type[] bounds = typeVariable.getBounds();
                       for (Type bound : bounds) {
                           Class boundClass = (Class) bound;
                           System.out.println("泛型类型参数的上界为:" + boundClass.getName()); // 打印:java.lang.Number
                       }
                   }
                }
            }
        }
    }
}

打印结果:

type = com.java.advanced.features.reflect.genericsclass.blog.Generator<T>
泛型类型参数的名字为 T
泛型类型参数的上界为:java.lang.Number

上面的测试代码看着可能觉得有点多,我们一点一点来看,肯定能够全部理解的。

首先,看获取 ParameterizedType 的部分:

// 获取此接口直接继承的接口对应的 Type 对象数组
Type[] types = NumberGenerator.class.getGenericInterfaces();
for (Type type : types) {
    System.out.println("type = " + type); // 打印:Generator<T>
    if (type instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) type;

大家不要去想 for-each 循环,我们这里 types 数组的长度就是 1,因为 NumberGenerator 直接继承的接口只有一个,就是 Generator
看一下这里获取到的 type 的值:

type = com.java.advanced.features.reflect.genericsclass.blog.Generator<T>

Generator<T> 说明 type 是一个泛型类型,即参数化类型,对应的是 ParameterizedType。因此,可以通过 type instanceof ParameterizedType 的条件判断,进而把 type 强制类型转换为 ParameterizedType 类型。

接着,调用 getActualTypeArguments() 方法,获取替换了泛型类型参数的实际类型的 Type 对象数组:

// 获取替换了泛型类型参数的实际类型的 Type 对象数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

我们这里 parameterizedType,即对应 Generator<T>。其实,这里并没有使用实际类型替换泛型类型参数,只是传入了一个类型参数而已。

因为 Generator<T> 只有一个类型参数,所以 actualTypeArguments 数组的长度是 1。

最后,看 TypeVariable 的部分:

if( actualTypeArgument instanceof TypeVariable) {
    TypeVariable typeVariable = (TypeVariable) actualTypeArgument;
    System.out.println("泛型类型参数的名字为 " + typeVariable.getName()); // 打印:T
    Type[] bounds = typeVariable.getBounds();
    for (Type bound : bounds) {
        Class boundClass = (Class) bound;
        System.out.println("泛型类型参数的上界为:" + boundClass.getName()); // 打印:java.lang.Number
    }
}

打印信息:

泛型类型参数的名字为 T
泛型类型参数的上界为:java.lang.Number

判断 actualTypeArgument 这个 Type 对象是否是 TypeVariable,通过判断;接着把 actualTypeArgument 强制类型转换为 TypeVariable 类型。

调用 TypeVariable 的两个函数:

String getName();
Type[] getBounds();

第一个函数是获取泛型类型参数的名字,这里获取到的是 T,这和定义 Generator<T> 时的类型参数是一样的;
第二个参数是获取泛型类型参数的上边界的 Type 数组。这里泛型类型参数 T 只有一个限定类型:Number

2.2.3 GenericArrayType

文档中的定义,GenericArrayType 表示一种数组类型,其组件类型为参数化类型或类型变量。

这句话告诉我们,组成 GenericArrayType 这个数组类型的元素要么是参数化类型ParameterizedType,要么是类型变量 TypeVariable。其他的类型如 ClassWildcardTypeGenericArrayType 都不可以。

下面我们通过代码来演示。

首先,定义一个 ListArrayGenerator 普通接口,继承于 Generator 泛型接口,并且使用 List<Integer>[] 这个实际类型替换了泛型类型参数:

public interface ListArrayGenerator extends Generator<List<Integer>[]>{
}

完整的测试代码如下:

public class _04_GenericArrayTypeTest {
    public static void main(String[] args) {
        Type[] types = ListArrayGenerator.class.getGenericInterfaces();
        for (Type type : types) {
            System.out.println("type = " + type); // Generator<List<Integer>[]>
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                // 获取替换了泛型类型参数的实际类型的Type数组
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    if (actualTypeArgument instanceof GenericArrayType) {
                        GenericArrayType genericArrayType = (GenericArrayType) actualTypeArgument;
                        System.out.println("替换了泛型类型参数的实际类型为: " + genericArrayType);
                        Type genericComponentType = genericArrayType.getGenericComponentType();
                        System.out.println("泛型数组类型的元素类型为: " + genericComponentType);
                    }
                }
            }
        }
    }
}

打印结果如下:

type = com.java.advanced.features.reflect.genericsclass.blog.Generator<java.util.List<java.lang.Integer>[]>
替换了泛型类型参数的实际类型为: java.util.List<java.lang.Integer>[]
泛型数组类型的元素类型为: java.util.List<java.lang.Integer>

同样地,我们仍是一行一行地看上面的代码,这样可以更好地理解。

首先,看获取 ParameterizedType 的部分:

Type[] types = ListArrayGenerator.class.getGenericInterfaces();
for (Type type : types) {
    System.out.println("type = " + type); // Generator<List<Integer>[]>
    if (type instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) type;

ListArrayGenerator 继承的接口 Generator<List<Integer>[]> 是一个泛型接口,即参数化类型,对应的是 ParameterizedType,因此,type 可以通过 type instanceof ParameterizedType 的判断,进而把 type 强转为 ParameterizedType 类型。

其次,调用 getActualTypeArguments() 方法,获取替换了泛型类型参数的实际类型的 Type 对象数组:

// 获取替换了泛型类型参数的实际类型的Type数组
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

这里的 parameterizedType,即 Generator<List<Integer>[]>,替换了泛型类型参数的实际类型是 List<Integer>[],对应的是 ArrayGenericType

最后,看 GenericArrayType 部分:

if (actualTypeArgument instanceof GenericArrayType) {
    GenericArrayType genericArrayType = (GenericArrayType) actualTypeArgument;
    System.out.println("替换了泛型类型参数的实际类型为: " + genericArrayType);
    Type genericComponentType = genericArrayType.getGenericComponentType();
    System.out.println("泛型数组类型的元素类型为: " + genericComponentType);
}

打印信息:

替换了泛型类型参数的实际类型为: java.util.List<java.lang.Integer>[]
泛型数组类型的元素类型为: java.util.List<java.lang.Integer>

上面的 actualTypeArgument,对应的是 GenericArrayType,因此通过 actualTypeArgument instanceof GenericArrayType 判断,把 actualTypeArgument 强转为 GenericArrayType 类型。
调用 GenericArrayType 的函数:

Type getGenericComponentType();

这个函数的作用是获取泛型数组类型的元素类型。这里得到的是 List<Integer>类型。
进一步,我们可以验证这里的元素类型是 ParameterizedType:

if (genericComponentType instanceof ParameterizedType) {
    System.out.println("泛型数组类型的元素类型是ParameterizedType");
}

2.2.4 WildcardType

文档上的定义,WildcardType 表示一个通配符类型表达式,如 ?? extends Number? super Integer

定义一个 NumberListGenerator 接口,它继承于 Generator 接口,并且使用 List<? extends Number> 来替换 Generator 泛型接口的类型参数 T

public interface NumberListGenerator extends Generator<List<? extends Number>>{
}

下面是完整的测试代码:

public class _05_WildcardTypeTest {
    public static void main(String[] args) {
        Type[] types = NumberListGenerator.class.getGenericInterfaces();
        for (Type type : types) {
            System.out.println("type = " + type); // Generator<List<? extends Number>>
            if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    if (actualTypeArgument instanceof ParameterizedType) {
                        ParameterizedType typeArgument = (ParameterizedType) actualTypeArgument;
                        Type[] actualTypeArguments1 = typeArgument.getActualTypeArguments();
                        for (Type type1 : actualTypeArguments1) {
                            if (type1 instanceof WildcardType) {
                                WildcardType wildcardType = (WildcardType) type1;
                                System.out.println("wildcardType = " + wildcardType);
                                Type[] lowerBounds = wildcardType.getLowerBounds();
                                for (Type lowerBound : lowerBounds) {
                                    System.out.println("lowerBound = " + lowerBound);
                                }
                                Type[] upperBounds = wildcardType.getUpperBounds();
                                for (Type upperBound : upperBounds) {
                                    System.out.println("upperBound = " + upperBound);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

这段代码长得有点可以了。不过,我们仍然是一点点地看,一定会弄明白的。

首先,看获取 ParameterizedType 的部分:

Type[] types = NumberListGenerator.class.getGenericInterfaces();
for (Type type : types) {
    System.out.println("type = " + type); // Generator<List<? extends Number>>
    if (type instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) type;

NumberListGenerator 继承的接口 Generator<List<? extends Number>> 是一个泛型接口,即参数化类型,对应的是 ParameterizedType,因此,type 可以通过 type instanceof ParameterizedType 的判断,进而把 type 强转为 ParameterizedType 类型。

其次,调用 getActualTypeArguments() 方法,获取替换了泛型类型参数的实际类型的 Type 对象数组:

Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();

这里的 parameterizedType,即 Generator<List<? extends Number>>,替换了泛型类型参数的实际类型是 List<? extends Number>,对应的是 ParameterizedType

接着,把上一步获取到的实际类型,通过类型转换为 ParameterizedType

for (Type actualTypeArgument : actualTypeArguments) {
    if (actualTypeArgument instanceof ParameterizedType) {
        ParameterizedType typeArgument = (ParameterizedType) actualTypeArgument;

这一步的 typeArgument 是一个 ParameterizedType 类型,对应的是 List<? extends Number>

接着,既然 typeArgument 是一个 ParameteizedType 类型,我们可以调用 getActualTypeArguments() 方法获取替换了泛型类型参数的实际类型:

Type[] actualTypeArguments1 = typeArgument.getActualTypeArguments();

这里的 typeArgument 是一个 ParameterizedType 类型,对应的是 List<? extends Number>;通过调用 getActualTypeArguments() 方法,获取到的就是替换了List<T> 泛型接口的类型参数 T 的实际类型,即 ? extends Number

接着,把上一步获取到的就是替换了List<T> 泛型接口的类型参数 T 的实际类型类型转换为 WildcardType 类型:

for (Type type1 : actualTypeArguments1) {
    if (type1 instanceof WildcardType) {
        WildcardType wildcardType = (WildcardType) type1;

最后,调用 WildcardType 的两个方法:

Type[] getUpperBounds();
Type[] getLowerBounds();

来获取上边界和下边界的信息。

代码如下:

Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type lowerBound : lowerBounds) {
    System.out.println("lowerBound = " + lowerBound);
}
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type upperBound : upperBounds) {
    System.out.println("upperBound = " + upperBound);
}

打印信息:

upperBound = class java.lang.Number

3. 最后

本文介绍了泛型类的周边信息获取,以及 Type 体系,希望能够帮助到大家。巩固好反射知识,对于阅读框架源码是很有帮助的。

文末的参考链接,大家也可以看一下,都是很不错的文章。

参考

猜你喜欢

转载自blog.csdn.net/willway_wang/article/details/106584902
今日推荐