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

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);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

3. 最後

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章