我在泛型篇中說到了泛型在JVM中是會被擦除的,不過擦除的類還是“殘留”了一些泛型的痕跡。比如Person<T>類,雖然擦除掉了泛型<T>,但是通過反射機制是可以獲得這些信息的,不光如此,Person類里面定義的泛型方法都可以通過反射獲得。讓我們用代碼來驗證一下。
在用代碼驗證之前,大象會對Person類稍微改造一下,增加一個泛型變量K以及一個泛型方法,其它的都保持不變。
public class Person<T, K extends Comparable<? super T> & Serializable>
extends SuperPerson<String> implements Handle<Date> {
......
public static <T extends Comparable<? super T>> T max(List<? extends T> list){
Iterator<? extends T> it = list.iterator();
T result = it.next();
while(it.hasNext()){
T t = it.next();
if(t.compareTo(result) > 0)
result = t;
}
return result;
}
......
}
下面可以開始寫代碼了嗎?別急,大象還想先把代碼中用到的幾個跟反射有關(guān)的API接口說明一下,這都是JDK5.0中為泛型新增的,然后還用到了一個JDK6.0提供的Modifier,它是一個枚舉類型,可以表示類、方法或字段的修飾符。
Type
它是所有類型的公共接口。包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型。ParameterizedType, TypeVariable, WildcardType,GenericArrayType這四個接口都是它的子接口。
GenericDeclaration
這個接口Class、Method、Constructor都有實現(xiàn),我們就是要用這個接口的getTypeParameters方法,它返回一個TypeVariable[]數(shù)組,這個數(shù)組里面就是我們定義的類型變量T和K,順序與我們聲明時一樣。如果用循環(huán)語句將數(shù)組打印出來,你會發(fā)現(xiàn)只會輸出T和K,這可不是我們想要的結(jié)果,那么想要獲得預(yù)期的結(jié)果怎么辦呢?請繼續(xù)往下看。
TypeVariable
它表示類型變量。比如T,比如K extends Comparable<? super T> & Serializable,這個接口里面有個getBounds()方法,它用來獲得類型變量上限的Type數(shù)組,如果沒有定義上限,則默認設(shè)定上限為Object,請注意TypeVariable是接口,實際得到的是TypeVariableImpl實現(xiàn)類,下面幾個接口都一樣。
拿T和K來說明,T沒有定義任何上限,所以它就有一個默認上限java.lang.Object,實際跟蹤代碼的時候你會發(fā)現(xiàn)T的bounds屬性為空,只有在調(diào)用了getBounds()方法后,才會有一個Type[1]數(shù)組[class java.lang.Object]。而對于K來說,調(diào)用了getBounds方法后,得到的數(shù)組是[java.lang.Comparable<? super T>, interface java.io.Serializable],它們的類型卻是不一樣的,第1個是ParameterizedType,而第二個是Class
ParameterizedType
ParameterizedType表示參數(shù)化類型,就是上面說的java.lang.Comparable<? super T>,再比如List<T>,List<String>,這些都叫參數(shù)化類型。得到Comparable<? super T>之后,再調(diào)用getRawType()與getActualTypeArguments()兩個方法,就可以得到聲明此參數(shù)化類型的類(java.lang.Comparable)和實際的類型參數(shù)數(shù)組([? super T]),而這個? super T又是一個WildcardType類型。
WildcardType
它用來描述通配符表達式,上面返回的? super T正好是這個類型。然后調(diào)用getUpperBounds()上限和getLowerBounds()下限這兩個方法,獲得類型變量?的限定類型(上下限),對于本例的通配符(?),它的上限為java.lang.Object,下限為T
通過上面幾個接口的分析,可以將Person類的泛型參數(shù)都解析出來,那么Person的超類以及實現(xiàn)的接口該怎么處理呢?Class類里面同樣在1.5版本加入了getGenericSuperclass()與getGenericInterfaces()兩個方法,用于返回帶參數(shù)化類型的超類與接口。
至此,通過上面這些接口和方法我們已經(jīng)可以把class Person后面的代碼都解析出來了,類里面的方法與解析類的泛型化參數(shù)類似,就不再贅述了。下圖就是通過反射將定義的Person類打印出來的結(jié)果。

本文主要是想通過反射機制來驗證在JVM虛擬機中獲得泛型的一些知識。一般實際使用的時候多數(shù)是通過反射獲取超類的泛型,或者通過反射調(diào)用方法,讀取/設(shè)置屬性值等等這些功能,最下面有示例源碼的下載。
反射雖然很有用處,但也不能濫用。首先用了反射就沒辦法在編譯時進行類型檢查,而且反射的代碼比較復(fù)雜不容易閱讀,不過好在現(xiàn)在有很多已經(jīng)封裝好的反射工具類,幫我們做了不少這方面的工作。最后也是最重要的一點是因為,使用反射會有一定的性能損耗,就是說會比直接調(diào)用方法要慢,至于慢多少,這個不好說,但肯定會慢,因此除非有必要,大象建議在一般情況下首先考慮用接口來代替反射。
以上這些都是本篇關(guān)于泛型相關(guān)的反射介紹,算是一個入門知識吧,有什么不對的,或不完善的地方,還請各位指出來,謝謝!
源碼下載: 泛型與反射
本文為菠蘿大象原創(chuàng),如要轉(zhuǎn)載請注明出處。http://www.aygfsteel.com/bolo