如何通过反射获得泛型的类型

王二北原创,转载请标明出处:来自王二北

在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阅读 211,496评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,187评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,091评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,458评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,542评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,802评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,945评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,709评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,158评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,502评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,637评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,300评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,911评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,744评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,982评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,344评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,500评论 2 348

推荐阅读更多精彩内容