【问题总结】重写equalshashCode时需要的问题和避坑经验总结

知识点回顾

开发Java都知道,我们在使用POJO对象存储信息时,一定要重写 equals 方法和hashCode 方法

  • 前者是为了解决当两个POJO对象执行equals时,对比具体数值信息,而不是直接对比对象本身。
  • 后者是为了解决因为在使用散列数据结构时,比如哈希表,我们希望相等的对象具有相等的哈希码。

具体可以参考CSDN文章,讲的很好

场景模拟复原

给出一个具体的场景,我们需要实现一个物资分配的功能,每一次前端都会传递一个物资列表,需要我们进行持久化,并且需要判断物资的变化情况。

  • 比如原物资分配了A、B,新物资分配了A、C、D,那么出了需要将物资修改为ACD,还需要判断本次变化减少了物资B,新增了物资C和D

因此,结合需求,我们就可以使用到equals方法,来判断两个物资是否发现变化。

代码简单模拟

物资对象如下:

@Data
public class Good extends BaseEntity {private String name;private BigDecimal amount;}

企业开发的实体对象一般会继承一个基类,简单模拟

public class BaseEntity {Long id;}

在实际开发场景中,经常会选择手动重写、IDEA快速生成、Lombok注解的方式来重写equals和hashcode方法,这些都是可行的。接下来的坑点排查和生成方式无关,以IDEA自动生成的方法为例。

@Override
public boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;if (!super.equals(o)) return false;Good good = (Good) o;return Objects.equals(name, good.name) && Objects.equals(amount, good.amount);
}@Override
public int hashCode() {return Objects.hash(super.hashCode(), name, amount);
}

坑点一,直接和查询得到的结果比对出错。

以下模拟物品db是从数据库中查询得到的物品,我们需要和dto进行对比。尝试复原模拟实际场景

Good db = new Good();
db.setId(100L);
db.setName("原油");
Good dto = new Good();
dto.setName("原油");System.out.println("两者是否相等:"+ db.equals(dto));

打印如下

Good(name=原油, amount=0)
Good(name=原油, amount=0)
两者是否相等:false

原因分析

虽然两个物品的信息虽然一模一样,但是由于从数据库查询得到的物品db是带有主键id的信息,我们会发现物品的equals方法中有调用了父级的equals方法,但是最终结果不符合预期

image-20240313140211443

解决方案

应该根据实际场景来判断是否需要调用父级的equals方法;比如该场景中,对比的是商品的名称和数量,id是不需要对比的

修改后的equals方法如下

@Override
public boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Good good = (Good) o;return Objects.equals(name, good.name) && Objects.equals(amount, good.amount);
}

重新运行结果如下,符合场景

Good(name=原油, amount=0)
Good(name=原油, amount=0)
两者是否相等:true

坑点二、数值对比异常

很多时候我们使用bigdecimal存储数值时,进入数据库也会有对应的decimal数据类型。

尝试复原这个场景如下

Good db = new Good();
db.setAmount(new BigDecimal("100.00"));
db.setId(100L);
db.setName("原油");
Good dto = new Good();
dto.setAmount(new BigDecimal(100.00));
dto.setName("原油");
System.out.println("两者是否相等:"+ db.equals(dto));
Good(name=原油, amount=100)
Good(name=原油, amount=100.00)
两者是否相等:false

虽然我们存储的数值是一样的,但是判断结果为false,出现异常。

原因分析

深究代码即可发现,不管是IDEA自动生成的equals方法,还是lomok提供的注解生成的方法,对于bigdecimal类型的处理都是调用BigDecimal的equals方法进行处理.。

image-20240313141739027

image-20240313141809740

解决方案

要对比BigDecimal官方的API推荐是使用CompareTo()方法。

image-20240313141918508

方法进行以下重写即可:

public boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Good good = (Good) o;boolean res = false;if (amount!=null && good.amount !=null){res = amount.compareTo(good.amount) == 0;}return Objects.equals(name, good.name) && res;
}

进一步简写

public boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Good good = (Good) o;return Objects.equals(name, good.name) &&(amount == null && good.amount == null ||amount != null && good.amount != null &&  amount.compareTo(good.amount) == 0);
}

进行测试,结果如下,符合预期。

Good(name=原油, amount=100.00)
Good(name=原油, amount=100)
两者是否相等:true

经验总结

  1. 重写equals方法应该要根据实际的业务场景,选择需要的字段进行重写。特别是在使用lombok或者IDEA快速一键生成时,一定要根据业务场景进行选择使用。
  2. 遇到一些特殊的数据类型时,切忌无脑使用equals方法,应该使用规定的方法进行比较。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/534999.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Flutter 面试题】怎么理解Flutter的Isolate?并发编程

【Flutter 面试题】怎么理解Flutter的Isolate?并发编程 文章目录 写在前面解答补充说明完整代码示例说明 写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,…

数据库系统概念(第二周 第一堂)

前言 本文的所有知识点、图片均来自《数据库系统概念》(黑宝书)、山东大学李晖老师PPT。不可用于商业用途转发。 回顾 上周最后一个知识点说到数据库三级模式结构,在这个结构里面我们设立了模式/内模式映像、内模式/外模式映像,主…

罐头鱼AI短视频矩阵获客|AI视频批量生成

罐头鱼AI传单功能操作说明,智能化提升您的视频营销效率! 在这个信息爆炸的时代,短视频已成为企业营销的重要方式之一。而为了更高效地进行视频营销,罐头鱼AI传单功能应运而生,为您提供全方位的视频管理和发布服务。 首…

3d模型渲染成线条怎么办?---模大狮模型网

将3D模型渲染成线条效果通常可以通过以下几种方法实现,具体取决于你使用的建模软件或渲染器: 使用渲染器的线条渲染功能: 许多渲染器(如Blender的Cycles渲染器、Maya的Arnold渲染器等)提供了线条渲染功能。通过在渲染器设置中启用线条渲染选…

生产环境是Linux,日志不好查?自己开发一个下载日志功能页面

有时候甲方爸爸的项目要部署内网,日志不能直接copy&#xff0c;还是linux系统。排查日志很不方便。 自己搞一个日志下载功能&#xff0c;如果是分布式的项目&#xff0c;还能把其他项目的日志也一起copy下来&#xff0c;来看。 public BiStateDTO<Object> logList(Requ…

MAE预训练的VIT

MAE预训练的VIT&#xff0c;参考这篇文章&#xff1a;Segment Anything论文和源码解读 - 知乎

UI 学习 二 可访问性 模式

一 颜色对比 颜色和对比度可以用来帮助用户看到和理解应用程序的内容&#xff0c;与正确的元素交互&#xff0c;并理解操作。 颜色可以帮助传达情绪、语气和关键信息。可以选择主色、辅助色和强调色来支持可用性。元素之间足够的颜色对比可以帮助低视力的用户看到和使用你的应…

移速u盘怎么恢复彻底删除的文件,移速u盘彻底删除的文件如何恢复

移速u盘怎么恢复彻底删除的文件&#xff1f;在日常使用中&#xff0c;我们经常会遇到需要删除U盘中的文件的情况。然而&#xff0c;有时候我们可能会不小心将重要的文件彻底删除&#xff0c;导致数据的丢失。对于移速U盘用户来说&#xff0c;如何恢复彻底删除的文件成为了一个备…

源达财源滚滚:炒股决策助手,助力投资之路

在当今这个信息节奏飞快的金融市场&#xff0c;每位投资者都怀揣着对成功的渴望&#xff0c;渴望找到一款能够助力自己的炒股软件。他们深知&#xff0c;唯有借助先进的工具和技术、服务&#xff0c;才能更好地把握市场脉搏&#xff0c;实现财富梦想。 源达财源滚滚是一款聚合了…

深入挖掘C语言之——联合

目录 联合的定义 联合的特点 联合的应用场景 在C语言中&#xff0c;联合&#xff08;Union&#xff09;是一种特殊的数据结构&#xff0c;它允许在同一内存地址存储不同类型的数据。与结构体&#xff08;Struct&#xff09;不同的是&#xff0c;联合中的所有成员共享同一块内…

无缝集成 MongoDB Relational Migrator,Tapdata 提供关系型到 MongoDB 实时迁移优化方案

在去年的 MongoDB 用户大会纽约站上&#xff0c;MongoDB 正式宣布全面推出新工具 MongoDB Relational Migrator&#xff08;MongoDB RM&#xff09;&#xff0c;用以简化应用程序迁移和转换——即从传统关系型数据模型到现代的文档数据模型&#xff0c;助力组织快速提升运营效率…

深入解析Condition的底层实现原理

一、原理概述 在AQS中&#xff0c;存在两个FIFO队列&#xff1a;同步队列和等待队列。等待队列是由Condition内部实现的&#xff0c;是一个虚拟的FIFO单向队列。 释义&#xff1a; AQS中tali和head主要构成了一个FIFO双向的同步队列&#xff0c;AQS中Condition构成了一个FIFO…