复制两个不同类型对象的相同字段的值
来源:互联网 发布:加强网络舆情监督工作 编辑:程序博客网 时间:2024/05/16 13:00
需求的来源是项目。
每次做项目,都要建立很多的数据体,有的是数据库表产生的ORM,有的是网络请求的参数体,有的是网络接口调用给的返回数据返回数据体。同一种数据,这几种数据具体的结构并不一样。
ORM往往跟数据表一致,并且做了映射。但是外键数据只有id,并不详细。
前端调用的数据往往要求比ORM更丰富,有些则不需要。虽说可以建立一些与ORM无关的字段来用,但是字段多了会很麻烦,所以需要建立专用的VO数据类。
接口请求的有些数据,比ORM的数据要少一些,并不需要前端传值,但是我们使用swagger做ApiDoc,不详细说明,前端也搞不清哪些必填,哪些可以不用,就连自己有时候也会搞乱,所以还是另外给前端建立一个request数据参数类比较合适。
但是这样一来,同一套数据就要在不同的数据对象之间读写不停,字段繁多的数据读写代码要写好多,看起来总觉得不舒服。
今天躺床上,一觉醒来,突然产生一种灵感:完全可以用反射来封装一个工具类,让程序自动判断匹配的字段,自动传值,不同的字段在做特殊处理,这样就会节省很多的代码。
说干就干,翻身起床,打开电脑,写一个demo。一会儿就写好了,调试通过。
一、建立一个实体类,模拟ORM插件生成那种,字段可以多一点。
@Entity(name = "tb_user")public class UserEntity { private int id; private String name; private String password; private Integer age; private String address; ... ...}
二、创建一个用于请求类,两个类之间的字段有交集
public class UserRequest { private String name; private String password; private String vcode; ......}三、开始写工具类EntityUtils.java,有详细的注释
public class EntityUtils { /** * 复制名称相同类型相同字段的值 * * @param obj * @param clazz2 * @param <T1> * @param <T2> * @return */ public static <T1, T2> T2 copyData(T1 obj, Class<T2> clazz2) { //1. 获取源数据的类 Class clazz1 = obj.getClass();//源数据类 //2. 创建一个目标数据实例 final T2 obj2 = getInstance(clazz2); //3. 获取clazz1和clazz2中的属性 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); //4. 遍历fields2 for (Field f1 : fields1) { //4-1. 遍历fields1,逐字段匹配 for (Field f2 : fields2) { // 复制字段 copyField(obj, obj2, f1, f2); } } return obj2; } /** * 按照字段表复制相同名称相同类型的字段的值 * * @param obj * @param clazz2 * @param fieldNames * @param <T1> * @param <T2> * @return */ public static <T1, T2> T2 copyData(T1 obj, Class<T2> clazz2, String[] fieldNames) { //1. 获取源数据的类 Class clazz1 = obj.getClass();//源数据类 //2. 创建一个目标数据实例 final T2 obj2 = getInstance(clazz2); //3. 获取clazz1和clazz2中的属性 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); //4. 遍历字段列表 for (String fieldName : fieldNames) { //5. 遍历fields1 for (Field f1 : fields1) { //找到这个字段(找不到就不用遍历fields2) if (fieldName.equals(f1.getName())) { //5-1. 遍历fields2,逐字段匹配 for (Field f2 : fields2) { //在fields2中也要有这个字段 if (fieldName.equals(f2.getName())) { //复制字段 copyField(obj, obj2, f1, f2); } } } } } return obj2; } /** * 复制相同名称相同类型的字段的值 * * @param obj * @param obj2 * @param f1 * @param f2 * @param <T1> * @param <T2> */ private static <T1, T2> void copyField(T1 obj, T2 obj2, Field f1, Field f2) { try { //字段名要相同,字段类型也要相同 if (f1.getName().equals(f2.getName()) & f1.getType().getName().equals(f2.getType().getName())) { System.out.println(f1.getName()); //3-2. 获取obj这个字段的值 f1.setAccessible(true); Object val = f1.get(obj); //3-3. 把这个值赋给obj2这个字段 f2.setAccessible(true); f2.set(obj2, val); //3-4. 访问权限还原 f2.setAccessible(false); f1.setAccessible(false); } } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * 获得泛型类的实例 * * @param tClass * @param <T> * @return */ public static <T> T getInstance(Class<T> tClass) { try { T t = tClass.newInstance(); return t; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; }}
我喜欢使用的时候干干净净,所以习惯把异常在封装的时候解决掉,不喜欢抛出去。除非项目结构做了异常异常统一处理,需要throw.
四、写一个测试方法,来测试一下。
@Test public void test() { UserRequest userRequest = new UserRequest(); userRequest.setName("kalychen"); userRequest.setPassword("gk123456"); userRequest.setVcode("265847"); String[] fields = {"password"}; UserEntity userEntity = EntityUtils.copyData(userRequest, UserEntity.class, fields); System.out.println(userEntity.getName()); System.out.println(userEntity.getPassword()); }
测试结果:
这测试的是带有字段列表的,可以看出没有在字段名称数组中出现的字段,即使是名称相同类型相同也不会复制。设计这样一个重载方法目的是方便控制。本来计划中还有一个使用注解来控制的重载,但是觉得未必会比前面两个更方便,所以放弃了。
今天是周末,感觉星期一我的项目代码又要删减好多了。繁琐的代码我看着会觉得很不舒服的。
补充一下,这些实体类都是没有继承的。
迭代了一下,对程序进行了优化和扩展
public class EntityUtils { /** * 复制名称相同类型相同的字段数据 * * @param sourceObj * @param clazz * @param <T1> * @param <T2> * @return */ public static <T1, T2> T2 copyData(T1 sourceObj, Class<T2> clazz) { //1. 获取源数据的类 Class<?> clazz1 = sourceObj.getClass(); //2. 创建一个目标数据对象 T2 targetObj = getInstance(clazz); //3. 复制两个对象相同的字段 copyData(sourceObj, targetObj); return targetObj; } /** * 根据字段列表复制字段的值 * * @param sourceObj * @param clazz * @param fields * @param <T1> * @param <T2> * @return */ public static <T1, T2> T2 copyData(T1 sourceObj, Class<T2> clazz, String[] fields) { //1. 获取源数据的类 Class<?> clazz1 = sourceObj.getClass(); //2. 创建一个目标数据对象 T2 targetObj = getInstance(clazz); //3. 获取两个类字段集合 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz.getDeclaredFields(); //4. 复制字段 copyFieldValue(sourceObj, targetObj, fields, fields1, fields2); return targetObj; } /** * 复制两个对象中相同字段的值 * * @param sourceObj * @param targetObj * @param <T1> * @param <T2> */ public static <T1, T2> void copyData(T1 sourceObj, T2 targetObj) { //1. 获取两个对象的类 Class<?> clazz1 = sourceObj.getClass(); Class<?> clazz2 = targetObj.getClass(); //3. 获取两个类字段集合 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); //4. 遍历fields1 for (Field f1 : fields1) { //4-1. 遍历fields2 for (Field f2 : fields2) { //4-2. 复制字段 copyFieldValue(sourceObj, targetObj, f1, f2); } } } /** * 根据字段表复制两个对象中相同字段的值 * * @param sourceObj * @param targetObj * @param fields * @param <T1> * @param <T2> */ public static <T1, T2> void copyData(T1 sourceObj, T2 targetObj, String[] fields) { //1. 获取源两个对象的类 Class<?> clazz1 = sourceObj.getClass(); Class<?> clazz2 = targetObj.getClass(); //3. 获取两个类字段集合 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); //4. 复制字段 copyFieldValue(sourceObj, targetObj, fields, fields1, fields2); } /** * 根据字段列表排除复制对象中相同字段的值 * 凡是在字段列表中出现的不进行复制 * * @param sourceObj * @param clazz * @param fields * @param <T1> * @param <T2> * @return */ public static <T1, T2> T2 copyDataExclude(T1 sourceObj, Class<T2> clazz, String[] fields) { //1. 获取源数据的类 Class<?> clazz1 = sourceObj.getClass(); //2. 创建一个目标数据对象 T2 targetObj = getInstance(clazz); //3. 获取两个类字段集合 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz.getDeclaredFields(); //4. 复制字段 copyFieldValueExclude(sourceObj, targetObj, fields, fields1, fields2); return targetObj; } /** * 根据字段列表排除复制两个对象中相同字段的值 * 凡是在字段列表中出现的不进行复制 * * @param sourceObj * @param targetObj * @param fields * @param <T1> * @param <T2> */ public static <T1, T2> void copyDataExclude(T1 sourceObj, T2 targetObj, String[] fields) { //1. 获取源两个对象的类 Class<?> clazz1 = sourceObj.getClass(); Class<?> clazz2 = targetObj.getClass(); //3. 获取两个类字段集合 Field[] fields1 = clazz1.getDeclaredFields(); Field[] fields2 = clazz2.getDeclaredFields(); //4. 复制字段 copyFieldValueExclude(sourceObj, targetObj, fields, fields1, fields2); } /** * 排除字段复制 * * @param sourceObj * @param targetObj * @param fields * @param fields1 * @param fields2 * @param <T1> * @param <T2> */ private static <T1, T2> void copyFieldValueExclude(T1 sourceObj, T2 targetObj, String[] fields, Field[] fields1, Field[] fields2) { for (String fieldName : fields) { //4-1. 遍历fields1 for (Field f1 : fields1) { //4-3. 是否匹配这个字段 if (fieldName.equals(f1.getName())) { //只要包含该字段就跳过 continue; } //4-4. 遍历fields2 for (Field f2 : fields2) { //4-5. 复制字段 copyFieldValue(sourceObj, targetObj, f1, f2); } } } } /** * 根据字段列表复制两个对象中相同字段的值 * * @param sourceObj * @param targetObj * @param fields * @param fields1 * @param fields2 * @param <T1> * @param <T2> */ private static <T1, T2> void copyFieldValue(T1 sourceObj, T2 targetObj, String[] fields, Field[] fields1, Field[] fields2) { for (String fieldName : fields) { //4-1. 遍历fields1 for (Field f1 : fields1) { //4-3. 是否匹配这个字段 if (fieldName.equals(f1.getName())) { //4-4. 遍历fields2 for (Field f2 : fields2) { //4-3. 是否匹配这个字段 if (fieldName.equals(f2.getName())) { //4-2. 复制字段 copyFieldValue(sourceObj, targetObj, f1, f2); } } } } } } /** * 复制字段的值 * * @param sourceObj * @param targetObj * @param field1 * @param field2 * @param <T1> * @param <T2> */ public static <T1, T2> void copyFieldValue(T1 sourceObj, T2 targetObj, Field field1, Field field2) { try { //1. 判断两个字段是否名称相同而且类型相同 if (field1.getName().equals(field2.getName()) && equalFieldsType(field1, field2)) { //2. 获取源数据字段的值 field1.setAccessible(true); Object value = field1.get(sourceObj); //3. 给目标数据字段赋值 field2.setAccessible(true); field2.set(targetObj, value); //4. 访问权限还原 field2.setAccessible(false); field1.setAccessible(false); } } catch (IllegalAccessException e) { e.printStackTrace(); } } /** * 获取一个泛型的实例 * * @param clazz * @param <T> * @return */ public static <T> T getInstance(Class<T> clazz) { try { return clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } /** * 判断两个字段的类型是否相同 * * @param field1 复制源 * @param field2 复制目标 * @return */ public static boolean equalFieldsType(Field field1, Field field2) { String fTypeName1 = field1.getType().getSimpleName(); String fTypeName2 = field2.getType().getSimpleName(); System.out.println(fTypeName1 + ":" + fTypeName2); //1. 处理基本数据类型和包装类 Map<String, String> map = new HashMap<String, String>(); map.put(int.class.getSimpleName(), Integer.class.getSimpleName()); map.put(byte.class.getSimpleName(), Byte.class.getSimpleName()); map.put(short.class.getSimpleName(), Short.class.getSimpleName()); map.put(char.class.getSimpleName(), Character.class.getSimpleName()); map.put(long.class.getSimpleName(), Long.class.getSimpleName()); map.put(float.class.getSimpleName(), Float.class.getSimpleName()); map.put(double.class.getSimpleName(), Double.class.getSimpleName()); map.put(boolean.class.getSimpleName(), Boolean.class.getSimpleName()); /** * 在涉及包装类的判断逻辑中,源数据不能是包装类 * 因为包装类一旦为null,会引发异常 */ Set<String> keySet = map.keySet(); for (String key : keySet) { if (key.equals(fTypeName1) && map.get(key).equals(fTypeName2)) { return true; } } //2. 名称相同、类型相同 if (fTypeName1.equals(fTypeName2)) { return true; } return false; }}
看看这个工具类的作用效果吧!!
我们做alipay的异步通知接口,要把异步通知结果发送到我们的消息中心,然后推送到手机端。alipay服务器发送的参数大约有28个,我们在支付处理服务建立了一个带有gson字段映射的类,很好地解析了接收的字段信息。转身,我们要将这个对象作为参数发送到消息中心。那么问题来了。我们用retrofit2调用消息中心的接口,gs使用gson将数据打包,类的字段又会根据字段映射解析为原来的样子。而消息中心接收数据是不去理会字段映射的。我们把建好的类复制到消息中心去接收,就会出现很多字段收不到的情况。我们总不能再去建一个类吧?或者改变原来的类?总之很麻烦,令人很不爽。没办法我们就只能一个字段一个字段去转移了。
不过,一旦用上这个工具类,原来的二三十行代码就可以直接缩减为一行了。我们把原来的类复制一个,去掉所有的字段映射。
看看效果:
//构建 AlipayNoticeVO alipayNoticeVO = EntityUtils.copyData(alipayNotice,AlipayNoticeVO.class);// alipayNoticeVO.setAppId(alipayNotice.getAppId());// alipayNoticeVO.setBody(alipayNotice.getBody());// alipayNoticeVO.setBuyerId(alipayNotice.getBuyerId());// alipayNoticeVO.setBuyerLogonId(alipayNotice.getBuyerLogonId());// alipayNoticeVO.setBuyerPayAmount(alipayNotice.getBuyerPayAmount());// alipayNoticeVO.setGmtClose(alipayNotice.getGmtClose());// alipayNoticeVO.setGmtCreate(alipayNotice.getGmtCreate());// alipayNoticeVO.setGmtPayment(alipayNotice.getGmtPayment());// alipayNoticeVO.setGmtRefund(alipayNotice.getGmtRefund());// alipayNoticeVO.setInvoiceAmount(alipayNotice.getInvoiceAmount());// alipayNoticeVO.setNotifyId(alipayNotice.getNotifyId());// alipayNoticeVO.setNotifyTime(alipayNotice.getNotifyTime());// alipayNoticeVO.setNotifyType(alipayNotice.getNotifyType());// alipayNoticeVO.setOutBizNo(alipayNotice.getOutBizNo());// alipayNoticeVO.setOutTradeNo(alipayNotice.getOutTradeNo());// alipayNoticeVO.setPointAmount(alipayNotice.getPointAmount());// alipayNoticeVO.setReceiptAmount(alipayNotice.getReceiptAmount());// alipayNoticeVO.setSellerEmail(alipayNotice.getSellerEmail());// alipayNoticeVO.setSendBackFee(alipayNotice.getSendBackFee());// alipayNoticeVO.setSellerId(alipayNotice.getSellerId());// alipayNoticeVO.setSign(alipayNotice.getSign());// alipayNoticeVO.setSignType(alipayNotice.getSignType());// alipayNoticeVO.setSubject(alipayNotice.getSubject());// alipayNoticeVO.setTotalAmount(alipayNotice.getTotalAmount());// alipayNoticeVO.setTradeStatus(alipayNotice.getTradeStatus());// alipayNoticeVO.setTradeNo(alipayNotice.getTradeNo());// alipayNoticeVO.setFundBillList(alipayNotice.getFundBillList()); return alipayNoticeVO;
注释掉的是原来的代码。被第一行取代。
- 复制两个不同类型对象的相同字段的值
- 相同对象的复制
- C#利用反射实现两个类的对象之间相同属性的值的复制
- 两个对象值相同,有相同的hash code
- 两个对象值相同,有相同的hash code
- 两个对象值相同,有相同的hash code
- 两个不同类型的重载
- 查询数据库内不同表间相同字段不同类型,不同长度的所有字段
- 查询一个表中的两个字段值相同的数据
- 查询一个表中的两个字段值相同的数据
- 取两张mysql表中分别两个字段相同的值
- 取两张mysql表中分别两个字段相同的值
- 把两个数据库中的两个表中,相同的字段的数据,从一个表复制到另一个表
- Java比较两个对象是否相同并获得不同的字段
- sql查询同一表格中,不同字段,但是两个字段的值相同的记录行
- 两个不同类型的重载“圆”
- 两个同步表的字段复制.Oracle.
- 比较两个对象的属性值是否相同
- IBM发布基于内存的人工智能计算架构
- JAVA中幸运抽奖
- linux 文件inode 详解
- 会话
- 【Scikit-Learn 中文文档】神经网络模块(监督的)- 监督学习
- 复制两个不同类型对象的相同字段的值
- 第11章-使用类
- 建造者模式
- 广州爱范儿面试经历
- 中国大学 MOOC 课程 《Python 语言程序设计》 课后练习(第 4 周)
- jQuery对象与DOM对象的转换
- 二叉树面试题
- SQL增、查、改语句
- 微信公众号开发--服务器配置(傻瓜入门教程)