反射机制是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
为业务数据类型, 不同的接口业务数据结构会不一样. 我们需要根据 ResponseDTO
和 T
, 反射出类型 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);
R的可视化接下来两节我会使用ggplot2 package分享一些常用的可视化操作。这一节主要包括条形图(柱状图)、折线图和散点图条形图(柱状图)条形图用来展现离散型单变量的分布特征,柱状图是垂直放置的条形图。至于饼图与玫瑰图,则可以将笛卡尔坐标系转换为极坐标系,由于数据分析中这两种图的使用频率不高,因此我在这里不做进一步分享Scores <- as.data.frame(c(rep(1, 27), rep(2, 23)))names(Scores) <- "分科情况"Scores$
边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括:深度上的不连续、表面方向不连续、物质属性变化和场景照明变化。 边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来
本文介绍RSA加解密中必须考虑到的密钥长度、明文长度和密文长度问题,对第一次接触RSA的开发人员来说,RSA算是比较复杂的算法,天缘以后还会补充几篇RSA基础知识专题文章,用最简单最通俗的语言描述RSA,让各位了解RSA算法本身其实也很简单,RSA的复杂度是因为数学家把效率和安全也考虑进去的缘故。本文先只谈密钥长度、明文长度和密文长度的概念知识,RSA的理论及示例等以后再谈。提到密钥,我们不得不...
名词解释:(1) SVM(Support Vector Machine)是从瓦普尼克(Vapnik)的统计学习理论发展而来的,主要针对小样本数据进行学习、分类和预测(有时也叫回归)的一种方法,能解决神 经网络不能解决的过学习问题。作者以为,类似的根据样本进行学习的方法还有基于案例的推理(Case-Based Reasoning),决策树归纳算法C4.5等,以后将详细阐述
macOS 在用了一段时间(到现在有一年的时间吧)之后,变得越来越慢了,比如要上传文件或保存文件,打开浏览文件或目录对话窗口就需要等几十秒,简直没办法忍受。解决方法:1. 打开终端输入 sudo nano /etc/auto_master 回车,输入密码;nano是一个字符终端的文本编辑器,有点像DOS下的editor程序。它比vi/vim要简单得多,比较适合Linux初学者使用。某些Linux发...
步骤一:添加一块新的硬盘查看磁盘代码:fdisk -l步骤二:新建分区;格式化分区新建分区:```cfdisk /dev/sdbn //新建p //主分区//敲三个回车 全部默认w //保存查看分区是否建立成功:fdisk -l格式化分区:mkfs.ext4 /dev/sdb1步骤三:新建backup目录;将/dev/sdb1挂载到/backup目录上;查看挂载mkdir /backupmount /dev/sdb1 /backupdf -H步骤
转载自:http://blog.51cto.com/cutebunny/624052上一节中我们介绍了一些基本概念和主要的API,本节开始我们将列举并分析一些实例。本文中的所有代码我都在vs2008下测试过,读者只需要替换少量的宏定义即可编译执行。 面对一块新的磁盘,我们首先要做的就是对其初始化。在系统中通过windows的磁盘管理完成这一点非常容易,但在程序中实现略微复杂。本节的示例...
发现放到CSDN之后排版全乱套了。。已经在我自己的站点重新排版: Github · 目录 导 航 1.引言 1.1.编写目的 1.2.阅读范围 1.3.声明 1.4.缩写词/名词解释 1.5.参考资料 2.嵌入式开发学习笔记 2.1.开发环境/测试环境 ...
切换面板在网页中算是非常常见的,如视频网站的电视剧分类标签、资讯网站的资讯分类等;这些交互效果都可以利用动态面板的几个状态来进行相互切换得来。下面来介绍一下如何制作这样的标签。步骤1:从部件库中拖拽一个动态面板到线框图编辑区中,并在“部件交互和注释”窗口中命名为“通知”RP 万能的标签切换面板" TITLE="Axure RP 万能的标签切换面板" />步骤2:双击动态面
if(CMAKE_CXX_COMPILER_FORCED) # The compiler configuration was forced by the user. # Assume the user has configured all compiler information. set(CMAKE_CXX_COMPILER_WORKS TRUE) return() endif()
若项目使用logback打印日志,那么异常信息不可以用 e.printStackTrace();原因: 这种是同步打印,而且占内存,容易造成锁死。开发中千万不用使用e.printStackTrace(); 会被骂死的~~...
SoapObject soapObject = (SoapObject) envelope.getResponse();使用上面这个, SoapObject result = (SoapObject)envelope.bodyIn;这个是服务器那边没有搞定:1、 INFO/System.out(2185): SoapFault - faultcode: 'soapenv