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

作者: 王二北 | 来源:发表于2018-01-05 17:28 被阅读29次

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

在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();
        }
         
    }
}

相关文章

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

    王二北原创,转载请标明出处:来自王二北 在java开发中,泛型通常可以用来做两件事儿:1、检验限制检验限制添加的或...

  • 【Java】【反射】泛型反射

    泛型反射 在运行时,泛型是无效的,所以可以通过反射在运行时将其他类型变量添加到集合,而不需要考虑泛型

  • 抽象工厂模式创建对象

    结合反射应用,使用class.forName反射创建对象,通过泛型约束参数类型新产品只需实现CommonAPI即可...

  • 泛型和反射在项目中的基本应用之一

    一般在抽取的公共类中会涉及到泛型,而泛型的类型需要用到反射类中的方法确定泛型类型。

  • Java泛型

    本文介绍的知识点 泛型是什么? 泛型的使用在反射中使用泛型在集合类中使用泛型 关于泛型擦除如何理解?如何避免泛型擦...

  • 【Java利器】JDK5-9新特性解析+代码 - JDK5

    1.泛型 通过引入泛型,我们将获得编译时类型的安全和运行时更小地抛出ClassCastExceptions的可能。...

  • 43.泛型上限

    泛型上限 泛型智能是Fruit类型或者Fruit类型的子类 泛型的作用:放如何类型 限制存放的类型

  • Java如何通过反射获取泛型的真实类型?

    一、背景 在Android项目中想封装retrofit提供get、post等公共的方法,如: 返回的统一数据格式:...

  • C#之反射

    为什么需要反射 有时不知道具体的类型,可以通过dll去得到类的对象; 某些特殊方法,传过来的是泛型类,需要通过反射...

  • 2018-03-16

    泛型:泛型优点: 1,编译时可以保证类型安全。 2,不用做类型转换,获得一定的性能提升。 泛型约束: where ...

网友评论

    本文标题:如何通过反射获得泛型的类型

    本文链接:https://www.haomeiwen.com/subject/zhpsnxtx.html