Java如何比较两个对象并获取不相等的字段详解

目录
  •  写在前面
  • 缘起
  • 实现
  • 使用方法
  • 扩展
  • 后记

 写在前面

在工作中,我们经常会遇到这样的需求——比较两个对象是否相等,如果不相等的话,取出不相等的字段。

以下这些场景都需要我们对一个对象进行比较:

  • 数据比对
  • 做单元测试断言对象是否相等
  • 前端要求对不相等的字段进行高亮显示

这种需求其实是非常简单的,但是如何优雅地解决这一类需求呢?

通常的做法是重写对象的 equals 方法。但是重写 equals 方法有很多缺点,例如:

  • 每次对象属性有变更,一定要记得再重写(放心,你一定会忘记的)
  • 每个对象只能有一个 equals 方法,但是可能你会需要不同的比对规则
  • 只能对比两个对象是否相等,无法具体知道哪个属性不等
  • 自动生成的 equals 方法无法基于 getter 方法进行比对
  • 对象来自第三方依赖,无法重写 equals 方法

因此,实现一个通用的比对器可以减少很多不必要的麻烦,帮助我们很好地完成这一类的需求。

缘起

我是在做数据同步的时候有这个需求,我要将数据库的数据通过一定的规则导入到 ES 中,导入完成之后,如何比对两边的数据是否一致呢?这时候一个好用的比对器就是我非常好的帮手。

另外,我在做单元测试的时候发现,经常会需要将被测方法的返回值和期望的结果做 assertEquals 断言这时这个比对器也非常有帮助。我发现很多同事经常会遇到类似的需求。

于是,我找时间自己实现了一下。

实现

使用反射对传入的对象进行比对,提供了基于字段的比较器和基于 Getter 方法的对比器,并且充分考虑扩展性,使用者可以重写字段的比对规则。功能相对简单,代码实现也不难,而且做了很多注释,具体实现可以直接查看源码。

项目地址:https://github.com/dadiyang/equator

UML图:

使用方法

因为已经上传到了 maven 仓库中,我们使用非常方便:

添加 maven 依赖

<dependency>
    <groupId>com.github.dadiyang</groupId>
    <artifactId>equator</artifactId>
    <version>1.0.3</version>
</dependency>

初始化并调用方法

Equator equator = new GetterBaseEquator();
User user1 = new User(...);
User user2 = new User(...);
// 判断属性是否完全相等
equator.isEquals(user1, user2);
// 获取不同的属性
List<FieldInfo> diff = equator.getDiffFields(user1, user2);

扩展

我们可以通过继承并重写 isFieldEquals 方法自定义比对规则,例如我们在做单元测试的时候,对于 Date 类型的字段的比对,通常数据库不保存毫秒数,而我们 new 出来的 Date 对象则包含了毫秒数,因此我们在对包含 Date 类型字段的对象做比对的时候需要忽略日期的毫秒数。这时就可以通过重写isFieldEquals 方法来自定义了:

/**
 * 日期在数据库不保存毫秒数,因此需要特殊处理,比对时间时,忽略毫秒数
 *
 * @author dadiyang
 * @date 2019/3/23
 */
public class MmInsensitiveEquator extends GetterBaseEquator {
    @Override
    protected boolean isFieldEquals(FieldInfo fieldInfo) {
        if (fieldInfo.getFirstVal() instanceof Date) {
            Date first = (Date) fieldInfo.getFirstVal();
            Date second = (Date) fieldInfo.getSecondVal();
            if (Objects.equals(first, second)) {
                return true;
            }
            // 忽略毫秒数
            return Objects.equals(Math.round(first.getTime() / 1000), Math.round(second.getTime() / 1000));
        }
        return super.isFieldEquals(fieldInfo);
    }
}

后记

对象比对是一个非常小的需求,通常我们只会写一个工具类来完成。但是写一个工具类在各个项目间随处拷贝,非常不优雅,给整个团队带来很多不必要的维护成本。而且扩展性比较差,有任何差异就需要写很多代码去实现。

这时,如果我们从具体解决某一个需求的视角上升到解决一类需求,那么就能想出更加通用和优雅的解决方案了。一个个具体的需求是无穷无尽的,以有限的人生去解决无限的需求,殆矣;但是将它们归类之后,我们会发现,需求的种类是有限的。

到此这篇关于Java如何比较两个对象并获取不相等字段的文章就介绍到这了,更多相关Java比较两个对象内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java各种比较对象的方式的对比总结

    一.==和!=操作符 让我们从==和!=开始可以分别判断两个Java对象是否相同的操作符. 1.1 原始类型(Primitives) 对于原始类型,相同意味着具有相等的值: assertThat(1 == 1).isTrue(); 感谢自动拆箱,在将原语值与其包装类型对应值进行比较时,也可以这样做: Integer a = new Integer(1); assertThat(1 == a).isTrue(); 如果两个整数的值不同,==运算符将返回false,而!=运算符将返回true. 1.

  • Java如何比较两个对象并获取不相等的字段详解

    目录  写在前面 缘起 实现 使用方法 扩展 后记  写在前面 在工作中,我们经常会遇到这样的需求--比较两个对象是否相等,如果不相等的话,取出不相等的字段. 以下这些场景都需要我们对一个对象进行比较: 数据比对 做单元测试断言对象是否相等 前端要求对不相等的字段进行高亮显示 这种需求其实是非常简单的,但是如何优雅地解决这一类需求呢? 通常的做法是重写对象的 equals 方法.但是重写 equals 方法有很多缺点,例如: 每次对象属性有变更,一定要记得再重写(放心,你一定会忘记的) 每个对象

  • Java对象深复制与浅复制实例详解

     Java对象深复制与浅复制实例详解 我们在遇到一些业务场景的时候经常需要对对象进行复制,对于对象的复制一般有两种方式,深复制和浅复制 浅复制:对象的复制仅是对象本身,对象引用的其它对方并不会复制. 深复制:对象的复制包含对象引用的对象. Java所有对象的基类提供了clone方法,但是这个方法是protected native修饰,因此只暴露给之类去重写,外部是无法直接调用的. 我们现在来测试两种复制,首选是浅复制,浅复制要实现Cloneable接口. // 课程对象 class Class

  • Java将json对象转换为map键值对案例详解

    本文的目的是把json串转成map键值对存储,而且只存储叶节点的数据 比如json数据如下: {responseHeader:{status:0,QTime:0},spellcheck:{suggestions:{中国:{numFound:9,startOffset:0,endOffset:2,suggestion:[中国工商银行, 中国人民, 中国国际, 中国农业, 中国市场, 中国经济, 中国人, 中国广播, 中国文化]}},collations:{collation:中国工商银行}}} 如

  • Java 获取泛型的类型实例详解

    Java 获取泛型的类型实例详解 Java 泛型实际上有很多缺陷,比如不能直接获取泛型的类型,不能获取带泛型类等. 以下方式是不正确的: ①.获取带泛型的类的类型 Class lstUClazz = List<User>.class ②获取局部变量泛型的类型 List<User> listUser = new ArrayList<User>(); Type genType = listUser.getClass().getClass().getGenericSuperc

  • Java 获取服务器环境的实例详解

    Java 获取服务器环境的实例详解 废话不多说,直接上代码,代码中的注释写的比较清楚,请大家好好看, package com.rapido.utils; import java.util.Properties; /** * 获取服务器环境信息 * @author X-rapido * */ public class SystemDataUtil { public static Properties getSystemProperties() { Properties props = System

  • 两种获取connectionString的方式案例详解

     两种获取connectionString的方式 1. public static string connectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; <connectionStrings> <add name="ConnectionString" connectionString="Data Sour

  • java并发编程专题(十)----(JUC原子类)基本类型详解

    这一节我们先来看一下基本类型: AtomicInteger, AtomicLong, AtomicBoolean.AtomicInteger和AtomicLong的使用方法差不多,AtomicBoolean因为比较简单所以方法比前两个都少,那我们这节主要挑AtomicLong来说,会使用一个,其余的大同小异. 1.原子操作与一般操作异同 我们在说原子操作之前为了有个对比为什么需要这些原子类而不是普通的基本数据类型就能满足我们的使用要求,那就不得不提原子操作不同的地方. 当你在操作一个普通变量时,

  • java并发编程专题(十一)----(JUC原子类)数组类型详解

    上一节我们介绍过三个基本类型的原子类,这次我们来看一下数组类型: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray.其中前两个的使用方式差不多,AtomicReferenceArray因为他的参数为引用数组,所以跟前两个的使用方式有所不同. 1.AtomicLongArray介绍 对于AtomicLongArray, AtomicIntegerArray我们还是只介绍一个,另一个使用方式大同小异. 我们先来看看AtomicLong

  • Java SpringBoot在RequestBody中高效的使用枚举参数原理案例详解

    在优雅的使用枚举参数(原理篇)中我们聊过,Spring对于不同的参数形式,会采用不同的处理类处理参数,这种形式,有些类似于策略模式.将针对不同参数形式的处理逻辑,拆分到不同处理类中,减少耦合和各种if-else逻辑.本文就来扒一扒,RequestBody参数中使用枚举参数的原理. 找入口 对 Spring 有一定基础的同学一定知道,请求入口是DispatcherServlet,所有的请求最终都会落到doDispatch方法中的ha.handle(processedRequest, respons

  • Java并发编程深入理解之Synchronized的使用及底层原理详解 下

    目录 一.synchronized锁优化 1.自旋锁与自适应自旋 2.锁消除 逃逸分析: 3.锁粗化 二.对象头内存布局 三.synchronized锁的膨胀升级过程 1.偏向锁 2.轻量级锁 3.重量级锁 4.各种锁的优缺点 接着上文<Java并发编程深入理解之Synchronized的使用及底层原理详解 上>继续介绍synchronized 一.synchronized锁优化 高效并发是从JDK 5升级到JDK 6后一项重要的改进项,HotSpot虚拟机开发团队在这个版本上花费了大量的资源

随机推荐