背景::项目中需要实现List中对象内容拷贝&目标对象中字段已有内容则不覆盖,没有现成的工具方法,只好自己写一个,记录一下
问题示例
拿User类举例
/**
* list1中有用户名称,list2中有用户年龄,希望达到的效果是list2将年龄拷贝到list1中
* 希望最终结果:
* [User(id=1, name=小明, age=19), User(id=2, name=小红, age=18)]
* 如果循环使用beanUtils.copyProperties(),则list1中的name则会被list2中的name覆盖,导致最终结果name为null
*/
//User
public class User{
private Integer id;
private String name;
private Integer age;
}
//list1
List<User> list1 = new ArrayList<>();
list1.add(new User().setId(1).setName("小明"));
list1.add(new User().setId(2).setName("小红"));
List<User> list2 = new ArrayList<>();
list2.add(new User().setId(1).setAge(19));
list2.add(new User().setId(2).setAge(18));
实现工具类
package com.it.data_analysis.common.util;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Objects;
/**
* list中对象拷贝,目标对象中字段有值则不进行覆盖.
* @param <T>
*/
public class ListCopyProperties<T> {
/**
* list拷贝.
* @param sources 资源对象.
* @param targets 目标对象.
* @param uniqueIndexField 唯一索引字段。
*/
public void entityListCopyProperties(List<T> sources, List<T> targets,String... uniqueIndexField) {
if(targets.isEmpty()){
targets.addAll(sources);
return;
}
try {
for (T source : sources) {
for (T target : targets) {
//唯一索引字段相等则开始拷贝.
if (checkField(source,target,uniqueIndexField)) {
//获取目标类所有字段.
for (Field declaredField : target.getClass().getDeclaredFields()) {
declaredField.setAccessible(true);
Object o = declaredField.get(target);
//字段类型为Object & 内容为空.
if (Object.class.isAssignableFrom(declaredField.getType()) && Objects.isNull(o)) {
Field declaredField1 = source.getClass().getDeclaredField(declaredField.getName());
declaredField1.setAccessible(true);
declaredField.set(target, declaredField1.get(source));
}
}
}
}
}
} catch (Exception e) {
throw new RuntimeException("entityListCopyProperties copy fail." ,e);
}
}
/**
* 判断字段是否相等.
* @param source 资源对象.
* @param target 目标对象.
* @param fields 唯一索引字段
* @return
*/
private boolean checkField(T source,T target,String... fields) throws NoSuchFieldException,IllegalAccessException {
for (String field : fields) {
Object sourceVal = null;
Object targetVal = null;
//获取目标类唯一索引字段,先在本类获取字段,如本类没有,则去父类获取。父类字段需为public.
try {
Field targetField = target.getClass().getDeclaredField(field);
targetField.setAccessible(true);
targetVal = targetField.get(target);
} catch (NoSuchFieldException e) {
targetVal = target.getClass().getField(field).get(target);
}
try {
Field sourceField = source.getClass().getDeclaredField(field);
sourceField.setAccessible(true);
sourceVal = sourceField.get(source);
} catch (NoSuchFieldException e) {
sourceVal = source.getClass().getField(field).get(source);
}
//判断字段是否相等.
if(sourceVal != null && targetVal != null && !sourceVal.equals(targetVal)){
return false;
}
}
return true;
}
}
这个方法实现了将一个对象列表中的属性值复制到另一个对象列表中的相应对象上的功能,其中唯一索引字段相等的对象才会进行属性值的复制。具体实现分为以下几个步骤:
1.首先判断目标对象列表是否为空,如果为空,则直接将源对象列表复制到目标对象列表中。
2.遍历源对象列表中的每个对象,对于每个对象,遍历目标对象列表中的每个对象,判断唯一索引字段是否相等,如果相等,则开始属性值的复制。
3.对于每个目标对象,遍历其所有字段,如果该字段类型为 Object 类型且值为空,则在源对象中查找同名字段并将其值赋给目标对象的该字段。
4.判断唯一索引字段是否相等的方法 checkField() 中,首先通过反射获取目标对象和源对象中的唯一索引字段的值,然后判断这些值是否相等,如果任意一个字段值不相等,则返回 false,表示这两个对象的唯一索引字段不相等,不进行属性值的复制。
这段代码中使用了反射来获取对象的字段值,因此需要处理反射异常。另外,该方法中使用了可变参数 uniqueIndexField 来指定唯一索引字段,可以根据实际需求传入相应的字段名。
测试
@Test
public void test(){
List<User> list1 = new ArrayList<>();
list1.add(new User().setId(1).setName("小明"));
list1.add(new User().setId(2).setName("小红"));
List<User> list2 = new ArrayList<>();
list2.add(new User().setId(1).setAge(19));
list2.add(new User().setId(2).setAge(18));
new ListCopyProperties<User>().entityListCopyProperties(list2,list1,"id");
System.out.println(list1);
//结果为:[User(id=1, name=小明, age=19), User(id=2, name=小红, age=18)]
}
此工具类为定制方法,可能不适用与所有List 拷贝需求,可以根据现有代码改写下,如有不清楚的地方可以在下方留言,我会尽可能帮助你,也希望大牛可以多多留言指导。