Java反射递归比较两个对象(对象中包括对象、List等)里面的不同字段

前言

很多时候,我们需要对两个对象做diff,如果字段一多,比较起来相当的麻烦,利用反射,可以快速的实现一些diff比较,尽量性能不好,这是空间换时间的一些做法。

正文

核心实现如下:

/**
 * modify field diff util
 * @author Cao Yong
 * @date 2021-05-28 9:48:12
 */
public class ModifyFieldsDiffUtil {

	/**
	 * get modify field diffs
	 * @param before before modify DTO
	 * @param after after modify DTO
	 * @param diffs diff list
	 * @param customerClass customer class, need adding all customer diff object
	 * @param <T> class type that diff
	 */
	public static <T> void diff(T before, T after, List<DiffContent> diffs, Class<?>... customerClass) {
		//can not be null for all
		if (before == null && after == null) {
			return;
		}
		//get all fields for none null
		T allFieldsObj = before == null ? after : before;
		List<Field> finalFields = getClassAllFields(allFieldsObj);
		//iterator field
		for (Field declaredField : finalFields) {
			getDiffInfo(before, after, diffs, declaredField, customerClass);
		}

	}

	/**
	 * get class fields and supper class fields
	 * @param obj object
	 * @return all fields
	 */
	private static List<Field> getClassAllFields(Object obj) {
		Field[] superFields = null;
		Class<?> superclass = obj.getClass().getSuperclass();
		//get supper declared fields
		if (superclass != null) {
			superFields = superclass.getDeclaredFields();
		}
		//get declared fields
		Field[] fields = obj.getClass().getDeclaredFields();
		//all fields
		Field[] finalFields;
		if (superFields != null && superFields.length > 0) {
			finalFields = new Field[superFields.length + fields.length];
			//copy all super field
			System.arraycopy(superFields, 0, finalFields, 0, superFields.length);
			//copy all self field
			System.arraycopy(fields, 0, finalFields, superFields.length, fields.length);
		} else {
			finalFields = fields;
		}
		return Arrays.stream(finalFields).filter(field -> field.isAnnotationPresent(DiffField.class)).collect(Collectors.toList());
	}

	/**
	 * get diff info
	 * @param before diff before object
	 * @param after diff after object
	 * @param diffs diffs ArrayList
	 * @param declaredField declared field
	 * @param customerClass customer class, need adding all customer diff object
	 */
	private static <T> void getDiffInfo(T before, T after, List<DiffContent> diffs, Field declaredField, Class<?>... customerClass) {
		final String uId = "serialVersionUID";
		//ignore serialVersionUID
		if (uId.equals(declaredField.getName())) {
			return;
		}
		Object beforeInvoke = null;
		Object afterInvoke = null;
		if (before != null) {
			try {
				//get diff before object by invoke
				beforeInvoke = new PropertyDescriptor(declaredField.getName(), before.getClass()).getReadMethod().invoke(before);
			} catch (Exception ignored) {
			}
		}

		if (after != null) {
			try {
				//get diff after object by invoke
				afterInvoke = new PropertyDescriptor(declaredField.getName(), after.getClass()).getReadMethod().invoke(after);
			} catch (Exception ignored) {
			}
		}
		//adding diffs
		addingDiff(diffs, declaredField, beforeInvoke, afterInvoke, customerClass);
		if (beforeInvoke != null && afterInvoke != null) {
			if (beforeInvoke instanceof ArrayList) {
				List<?> beforeList = (ArrayList<?>) beforeInvoke;
				List<?> afterList = (ArrayList<?>) afterInvoke;
				//break when size not equals
				if (afterList.size() != beforeList.size()) {
					return;
				}
				int index = 0;
				for (Object beforeObj : beforeList) {
					//avoid recursive reference
					if (beforeObj.getClass() == before.getClass()) {
						throw new RuntimeException("Can not recursive depend");
					}
					//recursive call
					diff(beforeObj, afterList.get(index), diffs, customerClass);
					index++;
				}
			}
		}
	}

	/**
	 * adding diffs
	 * @param diffs diffs ArrayList
	 * @param declaredField declared field
	 * @param beforeInvoke before invoke value
	 * @param afterInvoke after invoke value
	 * @param customerClass customer class, need adding all customer diff object
	 */
	private static <T> void addingDiff(List<DiffContent> diffs, Field declaredField, T beforeInvoke, T afterInvoke, Class<?>... customerClass) {
		//get field annotation
		DiffField annotation = declaredField.getAnnotation(DiffField.class);
		//adding string diff
		if (String.class.getName().equals(declaredField.getType().getName())) {
			String beforeField = beforeInvoke == null ? "" : (String) beforeInvoke;
			String afterField = afterInvoke == null ? "" : (String) afterInvoke;
			if (!beforeField.equals(afterField)) {
				DiffContent diff = new DiffContent();
				diff.setModifyField(annotation == null ? declaredField.getName() : annotation.value());
				diff.setModifyField(declaredField.getName());
				diff.setBeforeModification(beforeField);
				diff.setAfterModification(afterField);
				diffs.add(diff);
			}

		}
		//adding integer diff
		if (Integer.class.getName().equals(declaredField.getType().getName())) {
			Integer beforeField = beforeInvoke == null ? 0 : (Integer) beforeInvoke;
			Integer afterField = afterInvoke == null ? 0 : (Integer) afterInvoke;
			if (!beforeField.equals(afterField)) {
				DiffContent diff = new DiffContent();
				diff.setModifyField(annotation == null ? declaredField.getName() : annotation.value());
				diff.setBeforeModification(String.valueOf(beforeInvoke));
				diff.setAfterModification(String.valueOf(afterInvoke));
				diffs.add(diff);
			}

		}
		//adding long diff
		if (Long.class.getName().equals(declaredField.getType().getName())) {
			Long beforeField = Objects.isNull(beforeInvoke) ? 0 : (Long) beforeInvoke;
			Long afterField = Objects.isNull(afterInvoke) ? 0 : (Long) afterInvoke;
			if (!Objects.equals(beforeField, afterField)) {
				DiffContent diff = new DiffContent();
				diff.setModifyField(annotation == null ? declaredField.getName() : annotation.value());
				diff.setBeforeModification(String.valueOf(beforeInvoke));
				diff.setAfterModification(String.valueOf(afterInvoke));
				diffs.add(diff);
			}
		}
		//adding customer class diff
		if (customerClass != null && customerClass.length > 0) {
			List<String> customerClassNames = Arrays.stream(customerClass).map(Class::getName).collect(Collectors.toList());
			if (customerClassNames.stream().anyMatch(declaredField.getType().getName()::equals)) {
				//recursive call
				diff(beforeInvoke, afterInvoke, diffs, customerClass);
			}
		}
	}
}

定义diff返回对象

/**
 *
 * @author Cao Yong
 * @date 2021-05-28日 9:52:23
 */
@Data
public class DiffContent implements Serializable {
	private static final long serialVersionUID = 646098769625590576L;
	private String modifyField;

	private String beforeModification;

	private String afterModification;

}

还可以通过自定义注解来标识字段

/**
 *
 * @author Cao Yong
 * @date 2020-05-28 21:05:23
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DiffField {
	/**
	 * parent value
	 * @return parent value
	 */
	String parentValue() default "";

	/**
	 * value
	 * @return value
	 */
	String value() default "";
}

注解使用示例:

/**
	 * 主键ID
	 */
	@DiffField(parentValue = "资料详情", value = "主键id")
	private Long id;

测试代码示例:

/**
 *
 * @author Cao Yong
 * @date 2021-05-28日 13:33:23
 */
public class DiffMainTest {
	public static void main(String[] args) throws Exception {
		DetailDto detail1 = new DetailDto()
		detail1.setName("test name1");
		DetailDto detail2 = new DetailDto()
		detail2.setName("test name2");
		List<DiffContent> diffs = new ArrayList<>();
		//RecordDto是对象中自己定义的对象
		ModifyFieldsDiffUtil.diff(detail1, detail2, diffs, RecordDto.class);
		System.out.println("diffs:{}===============" + diffs);
	}
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用递归反射来实现比较两个List集合对象属性值的差别,如果List包含List,则递归比较。以下是一个示例代码: ```java import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class CompareLists { public static void main(String[] args) { List<Student> list1 = new ArrayList<>(); list1.add(new Student("Tom", 12)); list1.add(new Student("Jerry", 15)); list1.add(new Student("Kate", 18)); List<Student> list2 = new ArrayList<>(); list2.add(new Student("Tom", 12)); list2.add(new Student("Jerry", 16)); list2.add(new Student("Lucy", 20)); compareLists(list1, list2); } private static <T> void compareLists(List<T> list1, List<T> list2) { if (list1.size() != list2.size()) { System.out.println("The size of the two lists is different."); return; } for (int i = 0; i < list1.size(); i++) { T obj1 = list1.get(i); T obj2 = list2.get(i); if (obj1.getClass() != obj2.getClass()) { System.out.println("The type of the two objects in the list is different."); return; } Field[] fields = obj1.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); try { Object value1 = field.get(obj1); Object value2 = field.get(obj2); if (value1 == null && value2 == null) { continue; } else if (value1 == null || value2 == null) { System.out.println("The value of field " + field.getName() + " is different."); return; } if (value1 instanceof List) { if (value2 instanceof List) { compareLists((List) value1, (List) value2); } else { System.out.println("The type of field " + field.getName() + " is different."); return; } } else { if (!value1.equals(value2)) { System.out.println("The value of field " + field.getName() + " is different."); return; } } } catch (IllegalAccessException e) { e.printStackTrace(); } } } System.out.println("The two lists are the same."); } static class Student { private String name; private int age; private List<String> hobbies; public Student(String name, int age) { this.name = name; this.age = age; this.hobbies = new ArrayList<>(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } } } ``` 在这个示例,首先定义了一个`Student`类来模拟对象,然后定义了两个List集合`list1`和`list2`,并将`Student`对象添加到这两个List集合。接下来调用`compareLists`方法来比较两个List集合。 在`compareLists`方法,首先比较两个List集合的大小是否相同,如果不同则直接返回。然后遍历这两个List集合对象比较两个对象的类型是否相同,如果不同则直接返回。接着获取这两个对象的属性列表,并遍历这个属性列表,比较两个对象的属性值是否相同。如果属性值是一个List集合,则递归调用`compareLists`方法来比较两个List集合对象。最后如果比较的过程没有发现不同之处,则输出这两个List集合相同的信息。 该示例代码使用了反射来获取对象的属性值,并使用泛型来支持比较任意类型的List集合。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值