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

本文讲述了在Java开发中如何正确重写equals和hashCode方法,特别是在物资分配场景中,如何处理主键ID、数值比较异常以及根据业务场景定制equals方法。作者给出了IDEA自动生成和BigDecimal比较的最佳实践。
摘要由CSDN通过智能技术生成

知识点回顾

开发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方法,应该使用规定的方法进行比较。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xcong_Zhu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值