改造BeanUtils,优雅实现List数据拷贝

BeanUtils.copyProperties();确实为我们做了很多事情,虽然不能完美完成深拷贝,但是对于 po、vo、dto 的拷贝已经足够用了。可还是有一些不够完美的地方。

不足几点如下:

. 不能拷贝 list,而拷贝 list 的情况又大量存在,因此会有许多重复代码。

复制代码
for (S source : sources) {
    T target = new T();
    copyProperties(source, target);
    list.add(target);
}

. 有一些简单的查询,仅仅需要转换一下 vo 也需要 new Vo()。

复制代码
public Vo findById(Integer id) {
 Vo vo = new Vo();
 Po po = dao.findById(id);
 copyProperties(po, vo);
 return vo;
}

. 这种拷贝方式是没有返回值的,jdk8 支持 stream() 操作之后,支持不是很友好,不方便 lambda 表达式的使用,因此我们决定通过集成 BeanUtils 类,自己造一个方便用的轮子。

使用

我们将新创建一个轮子 BeanConvertUtils,使用如下,当我们要转换 po、vo 时,只需要:

复制代码
// 使用前
public Vo findById(Integer id) {
 Vo vo = new Vo();
 Po po = dao.findById(id);
 copyProperties(po, vo);
 return vo;
}

// 使用后
public Vo findById(Integer id) {
 return BeanConvertUtils.converTo(dao.findById(id), Vo::new);
}

// 使用后,通过lambda表达式特殊处理个别字段
public Vo findById(Integer id) {
 return BeanConvertUtils.converTo(dao.findById(id), Vo::new, 
  (s, t) -> t.setName(s.getName))
 );
}

当我们要拷贝 list 的时候也很简单:

复制代码
// 使用前
public List<Vo> findAll() {
 List<Vo> vos = new ArrayList();
 List<Po> pos = dao.findAll();
 for (Po po : Pos) {
     Vo vo = new Vo();
     BeanUtis.copyProperties(po, vo);
     vos.add(vo);
    }
 return vos;
}

// 使用后
public List<Vo> findAll() {
 return BeanConvertUtils.converToList(dao.findAll(), Vo::new)
}

// 同样支持自定义lambda
public List<Vo> findAll() {
 return BeanConvertUtils.converToList(dao.findAll(), Vo::new,
  (s, t) -> t.setName(s.getName))
 )
}

代码如下

复制代码
/**
 * 转换对象工具
 *
 * @author bugpool
 */
public class BeanConvertUtils extends BeanUtils {

    public static <S, T> T convertTo(S source, Supplier<T> targetSupplier) {
        return convertTo(source, targetSupplier, null);
    }

    /**
     * 转换对象
     *
     * @param source         源对象
     * @param targetSupplier 目标对象供应方
     * @param callBack       回调方法
     * @param <S>            源对象类型
     * @param <T>            目标对象类型
     * @return 目标对象
     */
    public static <S, T> T convertTo(S source, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
        if (null == source || null == targetSupplier) {
            return null;
        }

        T target = targetSupplier.get();
        copyProperties(source, target);
        if (callBack != null) {
            callBack.callBack(source, target);
        }
        return target;
    }

    public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier) {
        return convertListTo(sources, targetSupplier, null);
    }

    /**
     * 转换对象
     *
     * @param sources        源对象list
     * @param targetSupplier 目标对象供应方
     * @param callBack       回调方法
     * @param <S>            源对象类型
     * @param <T>            目标对象类型
     * @return 目标对象list
     */
    public static <S, T> List<T> convertListTo(List<S> sources, Supplier<T> targetSupplier, ConvertCallBack<S, T> callBack) {
        if (null == sources || null == targetSupplier) {
            return null;
        }

        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T target = targetSupplier.get();
            copyProperties(source, target);
            if (callBack != null) {
                callBack.callBack(source, target);
            }
            list.add(target);
        }
        return list;
    }

    /**
     * 回调接口
     *
     * @param <S> 源对象类型
     * @param <T> 目标对象类型
     */
    @FunctionalInterface
    public interface ConvertCallBack<S, T> {
        void callBack(S t, T s);
    }
}

性能

由于只是 BeanUtils 的一个封装,跟原来的代码性能几乎差不多,如果要说差一点也没错,毕竟多了一层函数堆栈的调用,但是基本可以忽略不计。主要的性能还是由 BeanUtils 决定。

提醒

不知道大家对这个 BeanConvertUtils 工具类感觉怎么样,自己在项目中倒是大量使用,也很方便。

但是有两点要提醒:

  • 此方法依旧不能解决深层次的深拷贝问题,详细的可以 google 一下 BeanUtils 的深拷贝问题。

  • 如果 source 或者 targetSupplier 只要有一个为 null,本工具类不像 BeanUtils 一样抛出异常,而是返回 null,因为笔者认为调用方如果把 null 进行准换,那就是想转换为 null,为不为空应该由调用方自己负责。

相关推荐
超级小忍1 分钟前
从零开始:JDK 在 Windows、macOS 和 Linux 上的下载、安装与环境变量配置
java·windows·macos
.鸣9 分钟前
Java学习笔记:IDEA简单使用技巧
java·学习
2501_9167665413 分钟前
【IDEA2017】使用设置+创建项目的不同方式
java·intellij-idea
kyle~13 分钟前
C++---多态(一个接口多种实现)
java·开发语言·c++
funfan051717 分钟前
IDEA基础配置优化指南(中英双版)
java·ide·intellij-idea
罗小爬EX18 分钟前
在IDEA中设置新建Java类时自动添加类注释
java·intellij-idea
vvilkim33 分钟前
深入理解 Spring Boot Starter:简化依赖管理与自动配置的利器
java·前端·spring boot
柯南二号40 分钟前
【Java后端】【可直接落地的 Redis 分布式锁实现】
java·redis·分布式
1点东西1 小时前
新来的同事问我当进程/机器突然停止时,finally 到底会不会执行?
java·后端·程序员
Aspartame~2 小时前
K8s的相关知识总结
java·容器·kubernetes