这四种对象属性拷贝方式,你都知道吗?(第一种)

这四种对象属性拷贝方式,你都知道吗?(第一种)

一、定义


1.1、对象拷贝概念

Java中,数据类型分为值类型(基本数据类型)和引用类型,值类型包括int、double、byte、boolean、char等简单数据类型,引用类型包括类、接口、数组等复杂类型。

对象拷贝分为浅拷贝(浅克隆)与深拷贝(深克隆)

分类浅拷贝深拷贝
区别创建一个新对象,然后将当前对象的非静态字段
复制到该新对象,如果字段是值类型的,那么
对该字段执行复制;如果该字段是引用类型
的话,则复制引用但不复制引用的对象。因此,
原始对象及其副本引用同一个对象
创建一个新对象,然后将当前对象的非静态
字段复制到该新对象,无论该字段是值类型
的还是引用类型,都复制独立的一份。当你
修改其中一个对象的任何内容时,都不会影响
另一个对象的内容。



1.2、为什么需要拷贝对象


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


1.3、有哪些拷贝方式


1、org.springframework.beans.BeanUtils ;
2、org.springframework.cglib.beans.BeanCopier;
3、ma.glasnost.orika;
4、org.mapstruct(强烈推荐)。


二、BeanUtils

Spring中的BeanUtils,其中实现的方式很简单,就是对两个对象中相同名字的属性进行简单get/set,仅检查属性的可访问性。


BeanUtils源码

2.1、单个对象拷贝

我们把数据库查询出来的UserDO.java 拷贝到 UserVO.java。直接使用BeanUtils.copyProperties()方法。

@Test
public 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)

2.2、集合拷贝

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

@Test
public 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:[]

2.3、暴力拷贝

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


@Test
public 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)]

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

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

  • 定义一个函数式接口

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

@FunctionalInterface
public interface BeanUtilCopyCallBack <S, T> {

    /**
     * 定义默认回调方法
     * @param t
     * @param s
     */
    void callBack(S t, T s);
}
  • 封装一个工具类 BeanUtilCopy.java
public class BeanUtilCopy 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, BeanUtilCopyCallBack<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;
    }

}
  • 简单拷贝测试
 @Test
public 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 = BeanUtilCopy.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)]

注意:通过如上方法,我们基本实现了集合的拷贝,但是从返回结果我们可以发现:属性不同的字段无法拷贝。
UserDO.java 和UserVO.java 最后一个字段sex类型不一样,分别是:Integer/String

优化一下

  • 新增性别枚举类
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;
    }
}
  • 带特定转换的集合拷贝
@Test
public 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 = BeanUtilCopy.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类型的男生/女生。


后续三种拷贝方法下次更新,今天时间不够了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值