Java 獲取泛型類型

不太完美的泛型

大家在使用泛型開發(fā)的時候,有很多時候會遇到這樣的需求—— 獲取泛型具體的類。
比如現(xiàn)在有Service<T>,要想得到泛型T的類型,正常情況下,我們第一個想法就是通過T.class獲得,但得到的是那刺眼的下劃紅色波浪線。
那么如何才能拿到泛型的類型?想要得到泛型的類型需要滿足哪些條件呢?

獲取到泛型類型的條件

  1. 必須具有真實類型的存在
  2. 泛型的類型是明確的

第一個很好理解,如果連要獲取的類都不存在,即未定義,那自然是獲取不到的;
第二個條件,舉個例子,假設存在User,那么List<User>就是明確的,List<T>是不明確的。

滿足上面兩點,就可以獲取泛型的類型了。

獲取泛型類型

首先看下java.lang.Class.java中與泛型相關的方法有哪些:

與泛型相關的方法

其中getGenericInfo()getGenericSignature0()都是私有方法,那么我們暫時就只能嘗試從getGenericInterfaces()getGenericSuperclass()這兩個方法入手了。

查看源碼上的注釋,大致作用如下:

  • getGenericInterfaces: 返回此類直接實現(xiàn)的所有接口的類型。接口的實現(xiàn)類通過該方法,可以獲取所實現(xiàn)的接口的泛型類型,比如:接口Service<T>,實現(xiàn)類UserServiceImpl<User>,能夠得到Service<User>
  • getGenericSuperclass: 返回此類所表示的實體的直接超類的類型。一個類的子類通過該方法,可以獲取超類的泛型類型,比如:SuperClass<T>,子類Main<User>,能夠得到SuperClass<User>

可以看出都跟繼承類/實現(xiàn)接口有關,因為在類或接口定義的類型參數(shù)(泛型)其實是不確定的,只有子類繼承或接口實現(xiàn)才能確定類型參數(shù)的具體類型。比如:接口Service<T>,類型參數(shù)T是不確定的,說白了其實就是個占位符,而實現(xiàn)類UserServiceImpl<User>才能確定類型參數(shù)TUser。

為了更好理解,先來看一段代碼:

package xxx.xxx.xxx;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

abstract class SuperClass<T> {
}

class Main extends SuperClass<User> {

    public static void main(String[] args) {
        // 獲取 Main 的超類 SuperClass 的簽名(攜帶泛型). 這里為: xxx.xxx.xxx.SuperClass<xxx.xxx.xxx.User>
        Type genericSuperclass = Main.class.getGenericSuperclass();
        // 強轉成 參數(shù)化類型 實體.
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        System.out.println(parameterizedType);

        // 獲取超類的泛型類型數(shù)組. 即SuperClass<User>的<>中的內容, 因為泛型可以有多個, 所以用數(shù)組表示
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        Type genericType = actualTypeArguments[0];
        Class<User> clazz = (Class<User>) genericType;
        System.out.println(clazz);
    }

}

class User {
}

打印結果:

xxx.xxx.xxx.SuperClass<xxx.xxx.xxx.User>
class xxx.xxx.xxx.User

上面的代碼,是通過繼承方式,明確類型,然后獲取泛型類。
總體思路是這樣的:先定義一個類SuperClass<T>,其中T為類型參數(shù),即泛型,這個時候要想獲取T的類型,顯然是不可能的,因為本來就是一個通用類型;但是通過繼承該類,并確定了泛型的類型,那么我們就能通過getGenericSuperclass()獲取到泛型的類型。

其實,上面的代碼只是為了方便測試才這么寫的,也比較容易理解,一般實戰(zhàn)的寫法是這樣的:

abstract class SuperClass<T> {

    public Class<T> getGenericClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

}

class Main extends SuperClass<User> {

    public static void main(String[] args) {
        System.out.println(new Main().getGenericClass());
    }

}

class User {
}

不出意外的話,會打印如下結果:

class xxx.xxx.xxx.User

需要注意的是,SuperClass<T>是一個抽象類,因為getGenericClass()方法是為它的子類服務的,SuperClass<T>本身的實例對象如果調用該方法,將會直接報錯或返回錯誤的結果,所以干脆定義成抽象類,避免被實例化。另外,為了防止方法被覆寫,可以使用private訪問修飾符或者定義為 final 方法。

當然多級繼承也是適用的,比如:

// 這里省略 SuperClass 和 User 的定義

class Main<T> extends SuperClass<T> {
}

class MultiInheritance extends Main<User> {
    public static void main(String[] args) {
        System.out.println(new MultiInheritance().getGenericClass());
    }
}

通過上面的代碼,基本可以了解通過getGenericSuperClass()獲取泛型類型的大概思路和原理,而getGenericInterfaces()的獲取思路也是大同小異。直接上實例代碼:

package xxx.xxx.xxx;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

interface ServiceA<T> {
}

interface ServiceB<T> {
}

class ServiceImpl implements ServiceA<EntityA>, ServiceB<EntityB> {

    public static void main(String[] args) {
        Type[] genericInterfaces = ServiceImpl.class.getGenericInterfaces();
        if (genericInterfaces.getClass().isAssignableFrom(ParameterizedType[].class)) {
            for (Type genericInterface : genericInterfaces) {
                ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                Type type = actualTypeArguments[0];
                if (type instanceof Class) {
                    Class<?> clazz = (Class<?>) type;
                    System.out.println(clazz);
                }
            }
        }
    }

}

class EntityA {
}

class EntityB {
}

控制臺打印結果如下:

class xxx.xxx.xxx.EntityA
class xxx.xxx.xxx.EntityB

擴展

很多情況下,Class被用來當作參數(shù),我們其實可以將帶泛型的類作為參數(shù)傳入,我們一般為了方便,很少去特定定義一個類,因此,我們可以借助匿名內部類的便利來達到這一目的。
首先看一段代碼:

package xxx.xxx.xxx;

import java.lang.reflect.ParameterizedType;

abstract class SuperClass<T> {

    public Class<T> getGenericClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
        return (Class<T>) parameterizedType.getActualTypeArguments()[0];
    }

    public static void main(String[] args) {
        System.out.println(new SuperClass<User>(){}.getGenericClass());
    }

}

其中SuperClass<User>(){}就是一個匿名內部類。當然上面只是為了展示匿名內部類,真正的應用可以參考fastjsonTypeRefrence

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容