在上篇中,我們簡單給大家講解了如何利用反射來獲取普通類型的類的使用,今天給大家講解下,有關如何使用反射來獲取泛型中的信息。提前提個醒,本篇文章內容稍難,大家可能需要多看幾篇。
這篇文章將大量用到泛型的知識,如果對泛型聲明及填充不太瞭解的同學,請先看完《夯實JAVA基本之一 —— 泛型詳解系列》
一、獲取泛型超類和接口的相信信息
在這部分內容中,我們將講述如何獲取泛型的超類和接口,把上篇中遺留下來的兩個函數先講完。1、獲取泛型超類相信信息
上篇中,我們講了,要獲取泛型類型的超類,要用到一個函數:
- //針對泛型父類而設計
- public Type getGenericSuperclass();
- //Point泛型類的實現
- public class Point<T> {
- private T x,y;
- public T getX() {
- return x;
- }
- public void setX(T x) {
- this.x = x;
- }
- public T getY() {
- return y;
- }
- public void setY(T y) {
- this.y = y;
- }
- }
- //PointImpl類的實現
- public class PointImpl extends Point<Integer> {
- }
下面, 我們將通過反射獲取PointImpl的父類的類型,以及PointImpl的填充類型
我們在沒看代碼之前,我們先看看結果,我們知道PointImpl的父類類型是Point,而PointImpl的填充類型應該是Integer.
然後我們再看看代碼:
- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG,"填充類型爲:" + parameterArgClass.getName());
- }
- //返回 Type 對象,表示聲明此類型的類或接口。
- Type type1 = parameterizedType.getRawType();
- Class class22 = (Class) type1;
- Log.d(TAG,"PointImpl的父類類型爲:"+class22.getName());
- }
從結果中,我們可以看到,先獲得到的是PointImpl在填充父類時的類型Integer,然後獲得的是PointImpl的父類類型。
下面先看如何獲取當前類在填充父類時的填充類型的:
對應代碼是這一塊:
- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG,"填充類型爲:" + parameterArgClass.getName());
- }
- }
下面我們對這塊分塊講解:
(1)、獲取泛型超類
- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
(2)、Type類型
我們先看看Type的源碼,看他自己是怎麼說的:- package java.lang.reflect;
- /**
- * Common interface implemented by all Java types.
- *
- * @since 1.5
- */
- public interface Type {
- // Empty
- }
這就有點坑爹了,再仔細查代碼會出現,Class繼承了這個接口:
- public final class Class<T> implements Serializable, AnnotatedElement, GenericDeclaration, Type {
- …………
- }
他就是用來標識,當前Class中所填充的類型的。意思是,當我們在填充一個泛型時,比如上面我們的:
- public class PointImpl extends Point<Integer> {
- }
爲了解決這個問題,Java的開發者,在Type的基礎上派生了另外幾個接口,分別來保存不同的類型,他們分別是:
- ParameterizedType:這就是上面我們代碼中用到的,他代表的是一個泛型類型,比如Point,它就是一個泛型類型。
我們在代碼中,利用:
- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
- TypeVariable:這個代表的就是泛型變量,例如Point,這裏面的T就是泛型變量,而如果我們利用一種方法獲得的對象是T,那它對應的類型就是TypeVariable;(這個類型的應用後面會細講)
- WildcardType:上面的TypeVariable對應的是泛型變量,而如果我們得到不是泛型變量,而是通配符比如:? extends Integer,那它對應的類型就是WildcardType;
- GenericArrayType:如果我們得到的是類似String[]這種數組形式的表達式,那它對應的類型就是GenericArrayType,非常值得注意的是如果type對應的是表達式是ArrayList這種的,這個type類型應該是ParameterizedType,而不是GenericArrayType,只有類似Integer[]這種的纔是GenericArrayType類型。
- 雖然我們後面會對TypeVariable,WildcardType進行講解,這裏還是先對他們三個類型對應的意義先總結一下,比如我們這裏的clazz.getGenericSuperclass(),得到的Type對應的是完整的泛型表達式即:Point,那它對應的類型就是ParameterizedType,如果我們得到的Type對應的表達式,僅僅是Point中用來填充泛型變量T的Integer,那這個Type對應的類型就是TypeVariable,如果我們得到的是依然是填充泛型變量T的填充類型,這而個填充類型卻是通配符?,那這個Type對應的類型就是WildcardType。這一段看不大明白也沒關係,後面還會再講。
(3)、ParameterizedType
上面我們已經提到當獲取的Type類型,對應的是一個完整泛型表達式的時候,比如,我們這裏獲取到的PointImpl.class的父類:- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
在ParameterizedType中有兩個極有用的函數:
- Type[] getActualTypeArguments();
- Type getRawType();
- getActualTypeArguments():用來返回當前泛型表達式中,用來填充泛型變量的真正值的列表。像我們這裏得到的Point,用來填充泛型變量T的是Integer類型,所以這裏返回的Integer類型所對應的Class對象。(有關這一段,下面會補充,這裏先看getRawType)
- getRawType():我們從我們上面的代碼中,也可以看到,它返回的值是com.harvic.blog_reflect_2.Point,所以它的意義就是聲明當前泛型表達式的類或者接口的Class對象。比如,我們這裏的type對應的是Point,而聲明Point這個泛型的當然是Point類型。所以返回的是Point.Class
下面我們再回過來看看getActualTypeArguments():
我們上面說到,這個函數將返回用來填充泛型變量真實參數列表。像我們這裏的是Point,將返回Integer對應的Class對象。而並不是所有的每次都會返回填充類型對應的Class對象。我們知道我們在填充一個泛型時,是存在各種可能的,比如Point,Point<? extends Number>,Point<ArrayList>,Point<ArrayList<? extend Number>>,等等
雖然我們沒辦法窮舉可能填充爲哪些類型,但我們知道Type類型是用來表示填充泛型變量的類型的,而繼承Type接口只有下面五個:Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType!
所以這也是Type[] getActualTypeArguments();中Type[]數組的所有可能取值!
好了,現在我們再回來看看我們的代碼
- Class<?> clazz = PointImpl.class;
- Type type = clazz.getGenericSuperclass();
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG,"填充類型爲:" + parameterArgClass.getName());
- }
- }
- if (type instanceof ParameterizedType) {
- …………
- }
- ParameterizedType parameterizedType = (ParameterizedType) type;
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG,"填充類型爲:" + parameterArgClass.getName());
- }
然後我們將得到的Type進行強轉成Class類型,所以parameterArgClass對應的值就是Integer.Class。所以我們利用parameterArgClass.getName():java.lang.Integer
(5)、getRawType()
最後,我們再來看看getRawType的用法:- Type type1 = parameterizedType.getRawType();
- Class class22 = (Class) type1;
- Log.d(TAG,"PointImpl的父類類型爲:"+class22.getName());
2、獲取所繼承泛型接口的相關信息
上泛我們也說到,獲取普通類所繼承的接口使用的是Class.getInterfaces()函數,如果要獲取泛型接口的對象需要用到:- //獲取泛型接口的方法
- public Type[] getGenericInterfaces();
這裏得到的一個Type數組,因爲我們一個類可以繼承多個接口,所以這裏的每一個type對應的就是我們所繼承的一個接口類型。
下面我們舉個例子來看這個接口的用法:
首先,生成一個泛型接口:
- public interface PointInterface<T,U> {
- }
然後,我們直接使用前面的PointImpl來繼承好了,就不再另寫其它類了:
- public class PointImpl extends Point<Integer> implements PointInterface<String,Double> {
- }
下面我們來看如何來獲取PointImpl所繼承的泛型接口的信息:
- Class<?> clazz = PointImpl.class;
- Type[] types = clazz.getGenericInterfaces();
- for (Type type:types) {
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
- //返回 Type 對象,表示聲明此類型的類或接口。
- Type type1 = parameterizedType.getRawType();
- Class class22 = (Class) type1;
- Log.d(TAG,"聲明此接口的類型爲:"+class22.getName());
- }
- }
首先,是獲得PointImpl.class所繼承接口的數組
- Class<?> clazz = PointImpl.class;
- Type[] types = clazz.getGenericInterfaces();
然後是利用for…each循環遍歷types中的每一個元素。
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
- …………
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
所以我們將它們強轉爲Class類型,然後通過parameterArgClass.getName()得到它們的完整路徑名。
最後通過parameterizedType.getRawType()獲取聲明PointInterface<String,Double>的接口類類型,雖然這裏得到的是Type,但我們聲明接口的是PointInterface.Class所以,也是Class類型,直接將其強轉爲Class即可。最後通過Class.getName()獲取其完整的路徑名。
- //返回 Type 對象,表示聲明此類型的類或接口。
- Type type1 = parameterizedType.getRawType();
- Class class22 = (Class) type1;
- Log.d(TAG,"聲明此接口的類型爲:"+class22.getName());
二、Type的五種類型
上面說們說過,Type接口是用來保存當前泛型被填充的類型的,它總共有五種類型:Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType在這上面的例子中,我們用到了Class,ParameterizedType;當type所代表的表達式是一個完整泛型時,比如Point,那這個Type類型就是ParameterizedType;如果type所代表的是一個確定的類,比如Integer,String,Double等,那這個type所對應的類型就是Class;強轉之後,得到的就是他們所對應的Class對象,即Integer.Class,String.Class,Double.Class等
前面我們說過,如果type對應的是一個泛型變量,即類似於T,或U這種還沒有被填充的泛型變量,那它的類型就是TypeVariable;而如果type對應的是一個通配符表達式,比如? extends Num,或者僅僅是一個通配符?,類似這種有通符符的類型就是WildcardType;
而如果type對應的類型是類似於String[]的數組,那它的類型就是GenericArrayType;
下面我們就來分別看看TypeVariable、WildcardType和GenericArrayType的用法
1、TypeVariable
我們上面說了,當type代表的類型是一個泛型變量時,它的類型就是TypeVariable。TypeVariable有兩個函數:- String getName();
- Type[] getBounds();
- getName:就是得到當前泛型變量的名稱;
- getBounds:返回表示此類型變量上邊界的 Type 對象的數組。如果沒有上邊界,則默認返回Object;
我們依然在PointInterface泛型接口上做文章:
- public interface PointInterface<T,U> {
- }
- public class PointGenericityImpl<T extends Number&Serializable> implements PointInterface<T,Integer> {
- }
我們再看一下如何獲取信息:
- Class<?> clazz = PointGenericityImpl.class;
- Type[] types = clazz.getGenericInterfaces();
- for (Type type:types) {
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- if(parameterArgType instanceof TypeVariable){
- TypeVariable typeVariable = (TypeVariable) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + typeVariable.getName());
- //返回表示此類型變量上邊界的 Type 對象的數組。
- Type[] typebounds = typeVariable.getBounds();
- for (Type bound:typebounds){
- Class<?> boundClass = (Class)bound;
- //如果不寫,則默認輸出Object,如果寫了,則輸出對應的
- Log.d(TAG, "bound爲:" + boundClass.getName());
- }
- }
- if (parameterArgType instanceof Class){
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
- }
- }
- }
依然是一坨複雜得像翔一樣的代碼,我們逐段來分析下;
首先,獲取PointGenericityImpl直接繼承的的泛型接口數組
- Class<?> clazz = PointGenericityImpl.class;
- Type[] types = clazz.getGenericInterfaces();
然後我們將這個元素強轉爲ParameterizedType,然後利用parameterizedType.getActualTypeArguments()得到PointInterface<T,U>中T和U被填充的真實類型對應的Type數組。
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
我們知道T是一個泛型變量,所以對應的類型應該是TypeVariable
而Integer則是一個具體的類,它對應的類型應該是Class
針對T:
- if(parameterArgType instanceof TypeVariable){
- TypeVariable typeVariable = (TypeVariable) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + typeVariable.getName());
- //返回表示此類型變量上邊界的 Type 對象的數組。
- Type[] typebounds = typeVariable.getBounds();
- for (Type bound:typebounds){
- Class<?> boundClass = (Class)bound;
- //如果不寫,則默認輸出Object,如果寫了,則輸出對應的
- Log.d(TAG, "bound爲:" + boundClass.getName());
- }
- }
然後,利用typeVariable.getBounds()得到T的限定條件:上邊界的數組。上邊界的意思就是extends關鍵字後面的限定條件。“上”的意思就是能取到的最大父類。最大父類當然是用extends關鍵字來限定的。我們知道這裏的T的限定條件是:<T extends Number&Serializable>,所以 Type[] typebounds = typeVariable.getBounds();所得到typebounds有兩個變量,一個是Number,一個是Serializable;這兩個都是具體的類型,所以我們可以直接將它們轉換爲Class類型,然後利用Class.getName()獲取它們完整的路徑名,結果如下:(有關上下邊界的意義下面在講WildcardType時會有圖文講解)
針對Integer:
再來看下代碼:
- Class<?> clazz = PointGenericityImpl.class;
- Type[] types = clazz.getGenericInterfaces();
- for (Type type:types) {
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //返回表示此類型實際類型參數的 Type 對象的數組
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- for (Type parameterArgType : actualTypeArguments) {
- …………
- if (parameterArgType instanceof Class){
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
- }
- }
- }
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- if (parameterArgType instanceof Class){
- Class parameterArgClass = (Class) parameterArgType;
- Log.d(TAG, "此接口的填充類型爲:" + parameterArgClass.getName());
- }
2、GenericArrayType
上面我們說過,當type對應的類型是類似於String[]、Integer[]等的數組時,那type的類型就是GenericArrayType;這裏要特別說明的如果type對應的是類似於ArrayList、List這樣的類型,那type的類型應該是ParameterizedType,而不是GenericArrayType,因爲ArrayList是一個泛型表達式。所以當且僅當type對應的類型是類似於String[]、Integer[]這樣的數組時,type的類型纔是GenericArrayType!我們先看看GenericArrayType的函數:
- Type getGenericComponentType();
好了,下面我們就舉個例子來看看GenericArrayType的用法
我們重新生成一個泛型接口PointSingleInterface:
- public interface PointSingleInterface<T> {
- }
然後生成一個類繼承這個接口:
- public class PointArrayImpl implements PointSingleInterface<Integer[]> {
- }
下面我們來看看如何獲取PointArrayImpl的接口信息:
- Class<?> clazz = PointArrayImpl.class;
- Type[] interfaces = clazz.getGenericInterfaces();
- for (Type type:interfaces){
- if (type instanceof ParameterizedType) {
- ParameterizedType pt = (ParameterizedType) type;
- Type[] actualArgs = pt.getActualTypeArguments();
- for (Type arg:actualArgs){
- if (arg instanceof GenericArrayType){
- GenericArrayType arrayType = (GenericArrayType)arg;
- Type comType = arrayType.getGenericComponentType();
- Class<?> typeClass = (Class)comType;
- Log.d(TAG,"數組類型爲:"+typeClass.getName());
- }
- }
- }
- }
依然看起來有點複雜,我們一點點來分析:
首先,通過clazz.getGenericInterfaces()獲取PointArrayImpl.class的接口對應的type列表
- Class<?> clazz = PointArrayImpl.class;
- Type[] interfaces = clazz.getGenericInterfaces();
- for (Type type:interfaces){
- if (type instanceof ParameterizedType) {
- ParameterizedType pt = (ParameterizedType) type;
- Type[] actualArgs = pt.getActualTypeArguments();
- for (Type arg:actualArgs){
- …………
- }
- }
- }
- Type[] actualArgs = pt.getActualTypeArguments()
我們知道當type對應的表達式是Integer[]時,這個type的類型就是GenericArrayType
- if (arg instanceof GenericArrayType){
- GenericArrayType arrayType = (GenericArrayType)arg;
- Type comType = arrayType.getGenericComponentType();
- Class<?> typeClass = (Class)comType;
- Log.d(TAG,"數組類型爲:"+typeClass.getName());
- }
好了,到這裏,我們已經講完了三種類型,下面開始講解最後一個也是最難的一種類型WildcardType!
3、WildcardType
(1)、概述
我們前面說過,當type所代表的表達式是類型通配符相關的表達式時,比如<? extends Integer>,<? super String>,或者<?>等,這個type的類型就是WildcardType!我們先來看看WildcardType的函數:
- //獲取上邊界對象列表
- Type[] getUpperBounds();
- //獲取下邊界對象列表
- Type[] getLowerBounds();
- getUpperBounds:獲取上邊界對象列表,上邊界就是使用extends關鍵定所做的的限定,如果沒有默認是Object;
- getLowerBounds:獲取下邊界對象列表,下邊界是指使用super關鍵字所做的限定,如果沒有,則爲Null
<? extends Integer>:這個通配符的上邊界就是Integer.Class,下邊界就是null
<? super String>:這個通配符的下邊界是String,上邊界就是Object;
有關上下邊界,大家可能很不好記,我畫個圖來給大家解釋下,上下邊界的含義:
看到這個類繼承圖,大家應該很容易就明白了,類繼承圖中,根結點始終是在祖先類,而且在繼承圖的上方,所以上方的就是上界,而子類是在下方,下方的就是下界。
而表現在代碼上,上界是繼承的關係,所以是<? extends Object>,而下界的則是<? super Double>
(2)、有關通配符的使用範圍
我們在《夯實JAVA基本之一——泛型詳解(2)》講過,通配符只是泛型變量的填充類型的一種,不能做爲泛型變量使用。在《夯實JAVA基本之一——泛型詳解(2)》中我們多次強調:通配符?只能出現在Box<?> box;中,其它位置都是不對的。
即只能出現在生成泛型實例時使用,其它位置都是不可以的。
尤其像下面這兩個,直接用來填充類中的泛型變量:
但下面這樣卻是允許的:
因爲,這裏的通配符Comparable<? extends Number>,只有來生成Comparable對象的,所以是允許使用的!大家一定要注意,通配符只能用來填充泛型類來生成對象。其它用途一概是錯誤的!
(3)、舉個例子
同樣,我們使用上面的PointSingleInterface泛型接口:
- public interface PointSingleInterface<T> {
- }
- public class PointWildcardImpl implements PointSingleInterface<Comparable<? extends Number>> {
- }
- Class<?> clazz = PointWildcardImpl.class;
- //此時的type對應PointSingleInterface<Comparable<? extends Number>>
- Type[] types = clazz.getGenericInterfaces();
- for (Type type:types) {
- if (type instanceof ParameterizedType) {
- ParameterizedType parameterizedType = (ParameterizedType) type;
- //得到填充PointSingleInterface的具體參數,即:Comparable<? extends Number>,仍然是一個ParameterizedType
- Type[] actualTypes = parameterizedType.getActualTypeArguments();
- for (Type actualType : actualTypes) {
- if (actualType instanceof ParameterizedType) {
- ParameterizedType ComparableType = (ParameterizedType) actualType;
- //對Comparable<? extends Number>再取填充參數,得到的type對應<? extends Number>,這個就是WildcardType了
- Type[] compareArgs = ComparableType.getActualTypeArguments();
- for (Type Arg:compareArgs){
- if(Arg instanceof WildcardType){
- //將得到的對應WildcardType的type強轉爲WildcardType的變量
- WildcardType wt = (WildcardType) Arg;
- //利用getLowerBounds得到下界,即派生自Super的限定,如果沒有派生自super則爲null
- Type[] lowerBounds = wt.getLowerBounds();
- for (Type bound:lowerBounds){
- Class<?> boundClass = (Class)bound;
- Log.d(TAG, "lowerBound爲:" + boundClass.getName());
- }
- //通過getUpperBounds得到上界,即派生自extends的限定,如果沒有,默認是Object
- Type[] upperBounds = wt.getUpperBounds();
- for (Type bound:upperBounds){
- Class<?> boundClass = (Class)bound;
- //如果不寫,則默認輸出Object,如果寫了,則輸出對應的
- Log.d(TAG, "upperBound爲:" + boundClass.getName());
- }
- }
- }
- }
- }
- }
- }
看到這一大段代碼,估計尿血的感覺都出來了。我們對它逐段分析:
首先獲取PointWildcardImpl.class所直接繼承的接口
- Class<?> clazz = PointWildcardImpl.class;
- Type[] types = clazz.getGenericInterfaces();
- ParameterizedType parameterizedType = (ParameterizedType) type;
- Type[] actualTypes = parameterizedType.getActualTypeArguments();
所以此時的actualTypes也僅有一個元素,它代表的表達式是Comparable<? extends Number>,明顯這依然是一個泛型,所以還得繼續往下剝
所以繼續將其中的actualType進行強轉,然後再得到它的填充參數:
- ParameterizedType ComparableType = (ParameterizedType) actualType;
- Type[] compareArgs = ComparableType.getActualTypeArguments();
此時的compareArgs列表中也僅有一個元素,這個元素代表的表達式是:<? extends Number>
這就是一個WildcardType類型了,然後是得到這個WildcardType的上下邊界信息了:
完整獲取上下邊界的代碼如下:(後面會分開講)
- for (Type Arg:compareArgs){
- if(Arg instanceof WildcardType){
- //將得到的對應WildcardType的type強轉爲WildcardType的變量
- WildcardType wt = (WildcardType) Arg;
- //利用getLowerBounds得到下界,即派生自Super的限定,如果沒有派生自super則爲null
- Type[] lowerBounds = wt.getLowerBounds();
- for (Type bound:lowerBounds){
- Class<?> boundClass = (Class)bound;
- Log.d(TAG, "lowerBound爲:" + boundClass.getName());
- }
- //通過getUpperBounds得到上界,即派生自extends的限定,如果沒有,默認是Object
- Type[] upperBounds = wt.getUpperBounds();
- for (Type bound:upperBounds){
- Class<?> boundClass = (Class)bound;
- //如果不寫,則默認輸出Object,如果寫了,則輸出對應的類名
- Log.d(TAG, "upperBound爲:" + boundClass.getName());
- }
- }
- }
- WildcardType wt = (WildcardType) Arg;
- //利用getLowerBounds得到下界,即派生自Super的限定,如果沒有派生自super則爲null
- Type[] lowerBounds = wt.getLowerBounds();
- for (Type bound:lowerBounds){
- Class<?> boundClass = (Class)bound;
- Log.d(TAG, "lowerBound爲:" + boundClass.getName());
- }
然後再看看如何得到上邊界:
- Type[] upperBounds = wt.getUpperBounds();
- for (Type bound:upperBounds){
- Class<?> boundClass = (Class)bound;
- //如果不寫,則默認輸出Object,如果寫了,則輸出對應的類名
- Log.d(TAG, "upperBound爲:" + boundClass.getName());
- }
結果如下:
好了,到這裏,所有有關type的類型就講完了,但我們上面是逐個分析當前type應該強轉爲哪種類型的,如果我們稍微疏忽分析錯了,或者,我們根本不知道它當前是哪種類型,這要怎麼辦,我們必須能寫出來一個統一的轉換函數出來!我們知道type所有的類型總共五種:Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType;所以我們利用遞規的方法來寫一個通用類型轉換函數出來。
4、Demo:寫一個通用類型轉換函數
我們這節就要寫一個通用的類型轉換函數了,1、實現parseClass(Class<?> c)函數
先寫一個parseClass的入口函數,用來得到他所直接繼承的泛型接口:
- private void parseClass(Class<?> c){
- parseTypeParameters(c.getGenericInterfaces());
- }
- private void parseTypeParameters(Type[] types){
- for(Type type:types){
- parseTypeParameter(type);
- }
- }
- private void parseTypeParameter(Type type){
- if(type instanceof Class){
- Class<?> c = (Class<?>) type;
- Log.d(TAG, c.getSimpleName());
- } else if(type instanceof TypeVariable){
- TypeVariable<?> tv = (TypeVariable<?>)type;
- Log.d(TAG, tv.getName());
- parseTypeParameters(tv.getBounds());
- } else if(type instanceof WildcardType){
- WildcardType wt = (WildcardType)type;
- Log.d(TAG, "?");
- parseTypeParameters(wt.getUpperBounds());
- parseTypeParameters(wt.getLowerBounds());
- } else if(type instanceof ParameterizedType){
- ParameterizedType pt = (ParameterizedType)type;
- Type t = pt.getOwnerType();
- if(t != null) {
- parseTypeParameter(t);
- }
- parseTypeParameter(pt.getRawType());
- parseTypeParameters(pt.getActualTypeArguments());
- } else if (type instanceof GenericArrayType){
- GenericArrayType arrayType = (GenericArrayType)type;
- Type t = arrayType.getGenericComponentType();
- parseTypeParameter(t);
- }
- }
2、使用parseClass(Class<?> c)
首先,我們還用上面用PointWildcardImpl類:- public class PointWildcardImpl implements PointSingleInterface<Comparable<? extends Number>> {
- }
- parseClass(PointWildcardImpl.class);
可以看到,打印出了每一個字段的名字。
好了,我們再回過頭來看看,parseTypeParameter(Type type)是怎麼寫的。
3、parseClass(Class<?> c)實現詳解
在parseClass(Class<?> c)中,最關鍵的部分是parseTypeParameter(Type type)函數,所以我們直接對parseTypeParameter(Type type)進行分析。我們知道,type總共有五種類型:Class,ParameterizedType,TypeVariable,WildcardType,GenericArrayType,所以我們在解析type時分別對它的每種類型進行判斷,然後分別解析即可:
- if(type instanceof Class){
- Class<?> c = (Class<?>) type;
- Log.d(TAG, c.getSimpleName());
- }
- } else if(type instanceof TypeVariable){
- TypeVariable<?> tv = (TypeVariable<?>)type;
- Log.d(TAG, tv.getName());
- parseTypeParameters(tv.getBounds());
- }
- } else if(type instanceof WildcardType){
- WildcardType wt = (WildcardType)type;
- Log.d(TAG, "?");
- parseTypeParameters(wt.getUpperBounds());
- parseTypeParameters(wt.getLowerBounds());
- }
- } else if(type instanceof ParameterizedType){
- ParameterizedType pt = (ParameterizedType)type;
- parseTypeParameter(pt.getRawType());
- parseTypeParameters(pt.getActualTypeArguments());
- }
- } else if (type instanceof GenericArrayType){
- GenericArrayType arrayType = (GenericArrayType)type;
- Type t = arrayType.getGenericComponentType();
- parseTypeParameter(t);
- }
好了,有關反射中相關泛型的部分就結束了,下面再總結一下,本篇文章中所涉及到的所有函數。
5、本文涉及函數:
- //獲取泛型超類的Type
- public Type getGenericSuperclass();
- //獲取泛型接口的方法
- public Type[] getGenericInterfaces();
- //獲取填充泛型變量的真實參數列表
- Type[] getActualTypeArguments();
- //返回聲明此當前泛型表達式的類或接口的Class對象
- Type getRawType();
- //就是得到當前泛型變量的名稱;
- String getName();
- //返回表示此類型變量上邊界的 Type 對象的數組。如果沒有上邊界,則默認返回Object;
- Type[] getBounds();
- //當前數組類型所對應的Type
- Type getGenericComponentType();
- //獲取通配符的上邊界對象列表
- Type[] getUpperBounds();
- //獲取通配符的下邊界對象列表
- Type[] getLowerBounds();