目錄
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 個子接口:GenericArrayType
,ParameterizedType
,TypeVariable
,WildcardType
;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
判斷,說明 type
是 ParameterizedType
的一個實例,再看一下 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 中,演示了 ParameterizedType
和 Class
的使用,本節會演示 TypeVariable
,GenericArrayType
和 WildcardType
的使用。
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
。其他的類型如 Class
,WildcardType
,GenericArrayType
都不可以。
下面我們通過代碼來演示。
首先,定義一個 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);
}
}
}
}
}
}
}
}
}