美文网首页程序员
Java反射-基于ParameterizedType实现泛型类类

Java反射-基于ParameterizedType实现泛型类类

作者: KAMIWei | 来源:发表于2018-01-08 15:20 被阅读8194次

反射机制是Java的一个非常实用的特性. 基于反射, 我们可以实现下面的接口

<T> T foo(..., Class<T> tClass);

将类型作为参数传入方法中, 方法可以根据具体的类实现不同的逻辑, 返回不同数据类型的结果. 这十分有利于减少代码的冗余度和耦合度, 在复杂多样的业务场景中非常有用.

然而, 当反射遇到泛型, 问题就变的棘手起来. 我们常常会遇到这么一个情景: 类型参数是继承了某个泛型类的子类的参数, 我们需要根据泛型基类和子类参数, 构造出子类类型. 与List<String>, Map<Integer, String> 等子类不一样, 这里的子类类型参数是动态指定的.

举个例子, 在开发中, 我们经常需要接入一些外部系统的Restful API. 通常, 除了业务数据部分外, 这些API的请求构建, 响应处理的逻辑都是一样的. 自然而然, 我们会想把接口的请求逻辑封装为一个方法来实现复用. 然而, 在处理响应的过程中, 我们需要对接口的返回结果进行反序列化. 假设外部接口返回的数据结构如下


@Data
public class ResponseDTO<T> {
  private String code;
  private String message;
  private T data;
}

T 为业务数据类型, 不同的接口业务数据结构会不一样. 我们需要根据 ResponseDTOT, 反射出类型 ResponseDTO<T> . 如果实现不了泛型类的参数化, 对于每一个业务接口的数据类型T, 我们都要显式继承 ResponseDTO 得到静态的子类类型, 未免特别冗余和不优雅.

幸好, 至 Java 1.7 开始, java.lang.reflect 提供了Interface ParameterizedType. 通过实现这个接口, 我们可以实现泛型类的类型的参数化, 代码如下:

public class ParameterizedTypeImpl implements ParameterizedType {

    private final Class raw;
    private final Type[] args;
    private final Type owner;

    public ParameterizedTypeImpl(Class raw, Type[] args, Type owner) {
      this.raw = raw;
      this.args = args != null ? args : new Type[0];
      this.owner = owner;
    }
    @Override
    public Type[] getActualTypeArguments() {
      return args;
    }
    @Override
    public Type getRawType() {
      return raw;
    }
    @Override
    public Type getOwnerType() {
      return owner;
    }
  }

利用这个类, 我们就可以实现参数化类型, 封装出某外部系统通用的HTTP调用方法:

GET请求

  private <T> T myHttpGet(HttpGet httpGet, Type dataType) {
    HttpClient httpClient = HttpClients.createDefault();
    try {
      HttpResponse response = httpClient.execute(httpGet);
      if (response.getStatusLine().getStatusCode()== HttpStatus.SC_OK) {
        String result = EntityUtils.toString(response.getEntity());

        Type parameterizedTypeClass = // 构造响应类型 ResponseDTO<T>
          new ParameterizedTypeImpl(ResponseDTO.class, new Type[]{dataType}, ResponseDTO.class);

        ResponseDTO<T> responseBody = gson.fromJson(result, parameterizedTypeClass); //使用GSON反序列化响应

        if (responseBody.getCode() != 0) {
          throw new MyException(
            String.format("[MY.HttpGet]请求失败, url=%s, details=%s",
              httpGet.getURI(), gson.toJson(responseBody.getMessage())),
            MyErrorCode.ES_REQUEST_FAIL);
        }

        return responseBody.getContent();

      }
      throw new MyException("[MY.HttpGet]" + response.getStatusLine().toString(), MyErrorCode.ES_REQUEST_FAIL);
    } catch (Exception ex) {
      throw new MyException("[MY.HttpGet]" + ex.getMessage(), MyErrorCode.ES_REQUEST_FAIL);
    }
  }

POST请求

  private <T> T myHttpPost(HttpPost httpPost, Type dataType) {
    HttpClient httpClient = HttpClients.createDefault();
    try {
      HttpResponse response = httpClient.execute(httpPost);
      if (response.getStatusLine().getStatusCode()== HttpStatus.SC_OK){
        String result= EntityUtils.toString(response.getEntity());

        Type responseTypeClass = // 构造响应类型  ResponseDTO<T>
          new ParameterizedTypeImp(ResponseDTO.class, new Type[]{dataType}, ResponseDTO.class);

        ResponseDTO<T> responseBody = gson.fromJson(result, responseTypeClass); // 使用GSON反序列化响应

        if(responseBody.getCode()!=0) {
          throw new MyException(
            String.format("[MY.HttpPost]请求失败, url=%s, details=%s",
              httpPost.getURI(), gson.toJson(responseBody.getMessage())),
            MyErrorCode.ES_REQUEST_FAIL);
        }
        return responseBody.getContent();
      }
      throw new MyException("[MY.HttpPost]" + response.getStatusLine().toString(), MyErrorCode.ES_REQUEST_FAIL);
    } catch (Exception ex) {
      ex.printStackTrace();
      throw new MyException("[MY.HttpPost]" + ex.getMessage(), MyErrorCode.ES_REQUEST_FAIL);
    }
  }

在调用时, 我们构造好请求对象和业务部分数据结构, 就可以得到对应的返回.

HttpPost httpPost = new HttpPost(host + "/api/dataA");
httpPost.addHeader("Content-Type", "application/json; charset=utf-8");
//...
StringEntity se = new StringEntity(gson.toJson(reqBody), "utf-8");
httpPost.setEntity(se);
try {
  DataA resbody = myHttpPost(httpPost, DataA.class);
} catch (Exception ex) {
  throw new MyException(
  "调用接口失败, " + ex.getMessage(),ErrorCode.ES_REQUEST_FAIL);
}

甚至, 泛型类的子类也可以参数化传入:

List<DataB> resbodyB = myHttpPost(httpPost, new TypeToken<List<DataB>>(){}.getType()); // 这里用到Gson的TypeToken

子类的参数类型也可以参数化, 使用ParameterizedTypeImp构造其类型即可:

Type typeDataCP = new ParameterizedTypeImp(DataC.class, new Type[]{tClass}, DataC.class); // Class<P> tClass
DataC<P> resbodyC = myHttpPost(httpPost, typeDataCP);

相关文章

  • Java反射-基于ParameterizedType实现泛型类类

    反射机制是Java的一个非常实用的特性. 基于反射, 我们可以实现下面的接口 将类型作为参数传入方法中, 方法可以...

  • 基于ParameterizedType实现泛型类类型参数化

    在上一篇中我们用到了这个方法: 在这篇中就来分析一下什么是ParameterizedType,它有什么作用。Par...

  • 泛型依赖注入

    泛型依赖注入 1.定义泛型 Java代码: 2.实现基类,声明泛型 Java代码: xml代码:

  • Java泛型

    参考:Java知识点总结(Java泛型) 自定义泛型类 自定义泛型接口 非泛型类中定义泛型方法 继承泛型类 通配符...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

  • 深入浅出泛型,框架设计的基础

    泛型在 Java 5 出现,实现了参数化类型,主要作用是使得类或接口更加通用。比如 Java 中的容器类,通过泛型...

  • 深入浅出泛型,框架设计的基础

    泛型在 Java 5 出现,实现了参数化类型,主要作用是使得类或接口更加通用。比如 Java 中的容器类,通过泛型...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • .NET Core中的泛型主机实现

    主机 Host 一、泛型主机 泛型主机主要基于 HostBuilder 类 对于普通的泛型主机,它的实现方式是: ...

  • 想理解泛型吗?看这一篇就够了!

    一、前言二、泛型类2.1 概述Java中泛型使用情况大致包括三种:泛型类、泛型接口、泛型方法 本节演示泛型类。 2...

网友评论

    本文标题:Java反射-基于ParameterizedType实现泛型类类

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