龙空技术网

BeanUtils 如何拷贝 List?

Layla星 755

前言:

而今姐妹们对“java克隆list”大概比较注重,小伙伴们都想要了解一些“java克隆list”的相关知识。那么小编同时在网络上网罗了一些对于“java克隆list””的相关内容,希望朋友们能喜欢,你们一起来了解一下吧!



一、背景

我们在DO、Model、VO层数据间可能经常转换数据:

Entity对应的是持久层数据结构(一般是数据库表的映射模型);Model 对应的是业务层的数据结构;VO 就是Controller和客户端交互的数据结构。

例如:数据库查询出来的用户信息(表的映射模型)是UserDO,但是我们需要传递给客户端的是UserVO,这时候就需要把UserDO实例的属性一个一个赋值到UserVO实例中。

在这些数据结构之间很大一部分属性都可能会相同,也可能不同。

二、数据拷贝2.1、数据模型UserDO.java

@Datapublic class UserDO {    private Long userId;    private String userName;    private Integer age;    private Integer sex;    public UserDO() {    }    public UserDO(Long userId, String userName, Integer age, Integer sex) {        this.userId = userId;        this.userName = userName;        this.age = age;        this.sex = sex;    }}
UserVO.java
@Datapublic class UserVO {    private Long userId;    private String userName;    private Integer age;    private String sex;}

注意: UserDO.java 和UserVO.java 最后一个字段sex类型不一样,分别是:Integer/String

2.2、常规使用-BeanUtils

Spring 提供了 org.springframework.beans.BeanUtils 类进行快速赋值。

例如:我们把数据库查询出来的UserDO.java 拷贝到 UserVO.java

@Testpublic void commonCopy() {    UserDO userDO = new UserDO(1L, "Van", 18, 1);    UserVO userVO = new UserVO();    BeanUtils.copyProperties(userDO, userVO);    log.info("userVO:{}",userVO);}

日志打印:

.... userVO:UserVO(userId=1, userName=Van, age=18, sex=null)

通过打印结果我们可以发现:除了类型不同的sex,其他数值都成功拷贝。

2.3、集合拷贝

刚刚拷贝的是一个对象,但是有时候我们想拷贝一组UerDO.java,是一个集合的时候就不能这样直接赋值了。如果还按照这种逻辑,如下:

@Testpublic void listCopyFalse() {    List<UserDO> userDOList = new ArrayList();    userDOList.add(new UserDO(1L, "Van", 18, 1));    userDOList.add(new UserDO(2L, "VanVan", 18, 2));    List<UserVO> userVOList = new ArrayList();    BeanUtils.copyProperties(userDOList, userVOList);    log.info("userVOList:{}",userVOList);}

日志打印如下:

.... userVOList:[]

通过日志可以发现,直接拷贝集合是无效的,那么怎么解决呢?

三、集合拷贝3.1、暴力拷贝(不推荐)

将需要拷贝的集合遍历,暴力拷贝。

测试方式

@Testpublic void listCopyCommon() {    List<UserDO> userDOList = new ArrayList();    userDOList.add(new UserDO(1L, "Van", 18, 1));    userDOList.add(new UserDO(2L, "VanVan", 20, 2));    List<UserVO> userVOList = new ArrayList();    userDOList.forEach(userDO ->{        UserVO userVO = new UserVO();        BeanUtils.copyProperties(userDO, userVO);        userVOList.add(userVO);    });    log.info("userVOList:{}",userVOList);}
拷贝结果
.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]

虽然该方式可以解决,但是一点都不优雅,特别是写起来麻烦。

3.2、优雅拷贝(本文推荐)

通过JDK 8 的函数式接口封装org.springframework.beans.BeanUtils

定义一个函数式接口

函数式接口里是可以包含默认方法,这里我们定义默认回调方法。

@FunctionalInterfacepublic interface BeanCopyUtilCallBack <S, T> {    /**     * 定义默认回调方法     * @param t     * @param s     */    void callBack(S t, T s);}
封装一个数据拷贝工具类 BeanCopyUtil.java
public class BeanCopyUtil extends BeanUtils {    /**     * 集合数据的拷贝     * @param sources: 数据源类     * @param target: 目标类::new(eg: UserVO::new)     * @return     */    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {        return copyListProperties(sources, target, null);    }    /**     * 带回调函数的集合数据的拷贝(可自定义字段拷贝规则)     * @param sources: 数据源类     * @param target: 目标类::new(eg: UserVO::new)     * @param callBack: 回调函数     * @return     */    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, BeanCopyUtilCallBack<S, T> callBack) {        List<T> list = new ArrayList<>(sources.size());        for (S source : sources) {            T t = target.get();            copyProperties(source, t);            list.add(t);            if (callBack != null) {                // 回调                callBack.callBack(source, t);            }        }        return list;    }}
简单拷贝测试
@Testpublic void listCopyUp() {    List<UserDO> userDOList = new ArrayList();    userDOList.add(new UserDO(1L, "Van", 18, 1));    userDOList.add(new UserDO(2L, "VanVan", 20, 2));    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new);    log.info("userVOList:{}",userVOList);}

打印结果:

.... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=null), UserVO(userId=2, userName=VanVan, age=20, sex=null)]

通过如上方法,我们基本实现了集合的拷贝,但是从返回结果我们可以发现:属性不同的字段无法拷贝。所以,我们这里需要借助刚定义的回调方法实现自定义转换。

性别枚举类

public enum SexEnum {    UNKNOW("未设置",0),    MEN("男生", 1),    WOMAN("女生",2),    ;    private String desc;    private int code;    SexEnum(String desc, int code) {        this.desc = desc;        this.code = code;    }    public static SexEnum getDescByCode(int code) {        SexEnum[] typeEnums = values();        for (SexEnum value : typeEnums) {            if (code == value.getCode()) {                return value;            }        }        return null;    }    public String getDesc() {        return desc;    }    public void setDesc(String desc) {        this.desc = desc;    }    public int getCode() {        return code;    }    public void setCode(int code) {        this.code = code;    }}
带回调函数的拷贝
@Testpublic void listCopyUpWithCallback() {    List<UserDO> userDOList = new ArrayList();    userDOList.add(new UserDO(1L, "Van", 18, 1));    userDOList.add(new UserDO(2L, "VanVan", 20, 2));    List<UserVO> userVOList = BeanCopyUtil.copyListProperties(userDOList, UserVO::new, (userDO, userVO) ->{        // 这里可以定义特定的转换规则        userVO.setSex(SexEnum.getDescByCode(userDO.getSex()).getDesc());    });    log.info("userVOList:{}",userVOList);}

打印结果:

... userVOList:[UserVO(userId=1, userName=Van, age=18, sex=男生), UserVO(userId=2, userName=VanVan, age=20, sex=女生)]

通过打印结果可以发现,UserDO.java 中Integer类型的sex复制到UserVO.java成了String类型的男生/女生。



想获得更多的学习资料,私信【学习资料】哦!

标签: #java克隆list #java 对象拷贝方法beanutils