资讯专栏INFORMATION COLUMN

Java如何比较两个对象并获取其中不相等的字段?

henry14 / 1473人阅读

摘要:以下这些场景都需要我们对一个对象进行比较数据比对做单元测试断言对象是否相等前端要求对不相等的字段进行高亮显示这种需求其实是非常简单的,但是如何优雅地解决这一类需求呢通常的做法是重写对象的方法。

声明:本文属原创文章,始发于 https://blog.csdn.net/dadiyan...。特此,同步发布到 sf,转载请注明出处。

写在前面

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

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

数据比对

单元测试断言对象是否相等

前端要求对不相等的字段进行高亮显示

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

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

每次对象属性有变更,一定要记得再重写(放心,你一定会忘记的

每个对象只能有一个 equals 方法,但是可能你会需要不同的比对规则

只能对比两个对象是否相等,无法具体知道哪个属性不等

自动生成的 equals 方法无法基于 getter 方法进行比对

对象来自第三方依赖,无法重写 equals 方法

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

缘起

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

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

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

实现

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

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

UML图:

使用方法

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

添加 maven 依赖


    com.github.dadiyang
    equator
    1.0.1

初始化并调用方法

Equator equator = new GetterBaseEquator();
User user1 = new User(...);
User user2 = new User(...);
// 判断属性是否完全相等
equator.isEquals(user1, user2);
// 获取不同的属性
List 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);
    }
}
后记

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

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

因此解决一类一类问题,能让我们摆脱无穷无尽的重复劳动,少加点班,多点时间陪陪家人哦。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77732.html

相关文章

  • 【金三银四】面试题之java基础

    摘要:中,任何未处理的受检查异常强制在子句中声明。运行时多态是面向对象最精髓的东西,要实现运行时多态需要方法重写子类继承父类并重写父类中已 1、简述Java程序编译和运行的过程:答:① Java编译程序将Java源程序翻译为JVM可执行代码--字节码,创建完源文件之后,程序会先被编译成 .class 文件。② 在编译好的java程序得到.class文件后,使用命令java 运行这个 .c...

    Yangyang 评论0 收藏0
  • 【金三银四】面试题之java基础

    摘要:中,任何未处理的受检查异常强制在子句中声明。运行时多态是面向对象最精髓的东西,要实现运行时多态需要方法重写子类继承父类并重写父类中已 1、简述Java程序编译和运行的过程:答:① Java编译程序将Java源程序翻译为JVM可执行代码--字节码,创建完源文件之后,程序会先被编译成 .class 文件。② 在编译好的java程序得到.class文件后,使用命令java 运行这个 .c...

    Barrior 评论0 收藏0
  • 第3章:抽象数据类型(ADT)和面向对象编程(OOP) 3.5 ADT和OOP中等价性

    摘要:抽象函数引发的关系是等价关系。所以当且仅当通过调用抽象数据类型的任何操作不能区分它们时,两个对象是相等的。必须为每个抽象数据类型适当地定义操作。一般来说,在面向对象编程中使用是一种陋习。 大纲 什么是等价性?为什么要讨论等价性?三种等价性的方式==与equals()不可变类型的等价性对象契约可变类型的等价性自动包装和等价性 什么是等价性?为什么要讨论等价性? ADT上的相等操作 ADT...

    Fundebug 评论0 收藏0
  • Flink 源码解析 —— 深度解析 Flink 是如何管理好内存

    摘要:减少垃圾收集压力因为所有长生命周期的数据都是在的管理内存中以二进制表示的,所以所有数据对象都是短暂的,甚至是可变的,并且可以重用。当然,并不是唯一一个基于且对二进制数据进行操作的数据处理系统。 showImg(https://segmentfault.com/img/remote/1460000020044119?w=1280&h=853); 前言 如今,许多用于分析大型数据集的开源系...

    Edison 评论0 收藏0
  • js中Java式继承

    摘要:中有三种不同的对象,三种对象的属性和行为和下方的类似构造函数对象构造函数为的类定义名称,任何添加到这个构造函数对象中的属性都是类字段和类方法。 JavaScript中的Java式继承 区别 Java类似的强类型面向对象语言,类为 实例字段 他们是基于实例的属性或变量,用以保存独立对象的状态 实例方法 他们是类的所有实例所共享的方法,由每个独立的实例调用 类字段 这些属性或者变量是属于类...

    SillyMonkey 评论0 收藏0

发表评论

0条评论

henry14

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<