如何通過反射獲得泛型的類型

王二北原創,轉載請標明出處:來自王二北

在java開發中,泛型通常可以用來做兩件事兒:
1、檢驗限制
檢驗限制添加的或者要處理的數據只能是泛型指定的類型。
比如List<String> list,這個list集合只能添加String類型的數據。
再比如,有一個抽象類Father:

public abstract  class Father {
    public abstract void execute();
}

有一個類Handler2,只處理繼承了Father類的對象:

public class Handler2<T extends Father> {
    public void execute(T t){
        t.execute();
    }
    
    public static void main(String[] args) {
        Handler2<Son> handler = new Handler2<Son>();
        Son sun = new Son();
        handler.execute(sun);
    }
}

在Handler2中只是調用了Father子類的execute方法,假如一個類沒有繼承Father類,則在編譯時就會報錯:

Handler2<User> handler = new Handler2<User>();

這行代碼會報錯:

Bound mismatch: The type User is not a valid substitute for the bounded parameter <T extends Father> of the type Handler2<T>

2、根據泛型指定的類型,進行參數的轉化
這里所說的參數轉換包括兩種:一種是類型的強制轉換,一種是數據的反序列化。
第一種就不說了(T)obj,這里重點說一下根據泛型進行數據的反序列化:
假設有這么一個需求,你需要設計一個RPC通信框架,RPC通信過程是這樣的,在A端,將一個T類型的對象序列化成一個json串,通過socket傳給B端,B端接收到這個消息后,將這個Json串反序列化成T類型的對象,進行處理。
先不說消息生產者A端,這里看一下消費者B端,B端不可能為所有類型都單獨寫一遍基于某個類型的數據接收和數轉換的實現方法,而是基于泛型,參數轉換類根據指定的泛型。
看下面例子,首先是一個父類Father:

public abstract class Father<T> {
    //當泛型是List、Set集合或者是Map時,clazz就是集合的類型,否則就是普通類的類型
    public Class clazz;
    //當泛型是List、Set類型時,valueClazz就是集合值的類型,Map時就是鍵值對中值的類型。當泛型為普通類型時,valueClazz為空
    public Class valueClazz;
    //當泛型是MAP時,keyClazz是鍵值對中key的類型,當泛型為普通類型時,keyClazz為空
    public Class keyClazz;
    public ParameterType parameterType;
     
    @SuppressWarnings("unchecked")
    public Father(){
        //獲得當前對象父類的類型,對于Son子類來說,就是Father類。Father類是一個帶有泛型的類型
        //所以getGenericSuperclass()返回的Type是一個ParameterizedType類型的實例
        ParameterizedType paramterizedType  = (ParameterizedType) this.getClass().getGenericSuperclass();
        //getActualTypeArguments用于獲得泛型類中<>中的實際類,返回值是一個數組,比如<T,Z>,則數組為{T.class,Z.class}
        Type type = paramterizedType.getActualTypeArguments()[0];
        //如果泛型中的T也是一個泛型的類型,比如List<User>, collection,Map等泛型
        if(ParameterizedType.class.isAssignableFrom(type.getClass())){
            ParameterizedType parameterizedType = (ParameterizedType)type;
            clazz = (Class<T>) parameterizedType.getRawType();
            //如果是map類型,設置key和value的類型
            //如果當前類實現了map接口
            if(Map.class.isAssignableFrom(clazz)){
                keyClazz = (Class) parameterizedType.getActualTypeArguments()[0];
                valueClazz = (Class) parameterizedType.getActualTypeArguments()[1];
                parameterType = ParameterType.MAP;
            //如果是collection集合,則設置集合value的類型
            }else{
                valueClazz = (Class) parameterizedType.getActualTypeArguments()[0];
                parameterType = ParameterType.COLLECTION;
            }
             
        }else{
            this.clazz = (Class<T>) paramterizedType.getActualTypeArguments()[0];
            parameterType = ParameterType.SIMPLE;
        }
    }
    /**
    * @Title: receiveAndExecuteMsg 
    * @Description: 接收并處理消息
    * @param @param message
    * @param @throws Exception    設定文件 
    * @return void    返回類型 
    * @throws
     */
    public void receiveAndExecuteMsg(String message)throws Exception{
        Object obj = null;
        //如果泛型是普通的引用類型
        if(parameterType==ParameterType.SIMPLE){
            obj = JsonConvertUtil.getObjFromJson(message, clazz);
        //如果是List<T>,Set<T>這種泛型
        }else if(parameterType==ParameterType.COLLECTION){
            obj = JsonConvertUtil.getListFromJosn(message, clazz, valueClazz);
        //如果是Map<E,T>這種泛型
        }else{
            obj = JsonConvertUtil.getMapFromJson(message, clazz, keyClazz, valueClazz);
        }
        execute((T)obj);
    }
     
    /**
     * 處理消息
    * @Title: execute 
    * @Description: TODO(這里用一句話描述這個方法的作用) 
    * @param @param t    設定文件 
    * @return void    返回類型 
    * @throws
     */
    public abstract void execute(T t);
}

然后是兩個子類,兩個子類都指定了不同的泛型:
public class Son extends Father<List<User>> {
    @Override
    public void execute(List<User> list) {
        System.out.println(list.size()+",name="+list.get(0));
    } 
}
public class Son2 extends Father<User> {
    @Override
    public void execute(User user) {
        System.out.println("name="+user.getName());
    }
}

下面是User實體類和枚舉類型ParameterType:
public class User {
    private int id;
    private String name;
  //省略了get/set
}
//標明泛型是什么類型
public enum ParameterType {
    SIMPLE("simple", "普通java類"),
    COLLECTION("collection", "List,Set等集合"),
    MAP("map", "map類型");
    private String value;
    private String desc;
     
    private ParameterType(String value,String desc){
        this.value = value;
        this.desc = desc;
    }
    //.....
}

最后,做一個測試:

public class TestMain {
 
    public static void main(String[] args) {
        List<User> userList = new ArrayList<User>();
        User user = new User();
        user.setId(1);
        user.setName("測試1");
        userList.add(user);
        //模擬一個json消息
        String message = JsonConvertUtil.getJsonFromObj(userList);
        Son son = new Son();
        try {
            //接收并處理消息
            son.receiveAndExecuteMsg(message);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //測試2
        User user2 = new User();
        user2.setName("測試2");
        //模擬一個json消息
        String message2 = JsonConvertUtil.getJsonFromObj(user2);
        Son2 son2 = new Son2();
        try {
            son2.receiveAndExecuteMsg(message2);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容