Java:自动equals()和hashCode()

23

equals()hashCode()实现在简单的数据POJO中,会使我的代码混乱,维护起来也很麻烦。

有哪些库可以自动处理这个问题?
由于性能原因,我更喜欢字节码插装而不是AOP方法。

更新:关于实现equals()和hashCode()的必要性已经讨论过了,以下是我的观点:

难道不是更好的方式是一开始就做好最小的努力,而不是在代码中添加hC/eq时再去修改吗?

编辑2022: 我已经转换到Kotlin。 Kotlin处理了大部分Java的样板文件,对于equals()的情况,请参见此页面:https://tedblob.com/kotlin-data-class/


5
没有任何库可以代替你处理equals方法 - 只有你自己知道什么样的对象是相等的。你使用的IDE为什么不会自动生成hashCode方法呢?为什么你需要对equalshashCode进行这么多维护工作呢? - Paul
1
我知道在Java社区中,为基本上每个POJO实现这些方法是很常见的,但我必须问一下:您是否真的将所有POJO类型用作哈希表中的键?您是否比较它们的相等性?如果不是,那么为什么还要费心呢? - Dan Tao
@OndraŽižka:Java 的表现力受到限制,因为缺乏对封装不可共享的可变对象状态和可共享的不可变对象状态以及封装共享对象标识符的引用之间的任何声明性区分。用于封装状态的引用应该匹配,如果所标识的对象的状态匹配。相比之下,仅当它们标识相同的对象时,封装标识符的引用才应该匹配。要定义等价关系,必须了解所比较的事物的目的。 - supercat
当AOP基于字节码插装时,您如何“更喜欢字节码插装而不是AOP方法”?您是否意味着编译时与加载时插装? - Vlastimil Ovčáčík
1
@VlastimilOvčáčík 是的,我认为从上下文中可以清楚地看出来:我更喜欢使用 Lombok 的方式而不是 AOP 的方式。 - Ondra Žižka
显示剩余2条评论
6个回答

16

Project Lombok提供了注解@EqualsAndHashCode,它将为你的Java类生成equals()hashCode()方法。当然,与手动实现这些方法相比,存在一些缺点,请务必阅读链接页面上的“小字”。


那可能是我正在寻找的东西。唯一遗憾的是它没有与NetBeans集成。 - Ondra Žižka

6

Objects.hashCodeObjects.hash

虽然不是您要求的万能方法,但从Java 7开始编写hashCode覆盖变得更加容易。

Java 7开始,Objects类提供了一些实用程序方法来生成哈希码值。

有关更多讨论,请参见相关问题上我的答案

单个成员,不容忍NULL

@Override
public int hashCode() {
    return this.member.hashCode() ;  // Throws NullPointerException if member variable is null.
}

单个成员,容忍空值

@Override
public int hashCode() {
    return Objects.hashCode( this.member ) ;  // Returns zero (0) if `this.member` is NULL, rather than throwing exception.
}

多成员

@Override
public int hashCode() {
    return Objects.hash( this.memberA , this.memberB , this.memberC  ) ;  // Hashes the result of all the passed objects’ individual hash codes.  
}

如果需要自动生成hashCodeequals,可以使用记录(record)来实现,详情请参考我的另一个回答


3
你可以使用谷歌的AutoValue库来自动生成带有equalshashCode的不可变值类。这些值类与Scala的case类或Lombok生成的类有些相似。
还有一篇文章介绍如何在IDE中使用它。

3

不是很...但感谢你让我终于检查它的功能。 - Ondra Žižka

1
Apache commons-lang 库提供了 HashCodeBuilder 和 EqualsBuilder,可以为您完成一些工作并缩短这些方法。甚至还有反射版本,可以根据 POJO 中的字段为您完成所有工作。然而,我不建议这样做。反射可能会很慢(虽然没有许多人想象的那么糟糕),您应该实现它们以确保仅考虑正确的字段进行相等比较。
我的问题是,您真的需要这样做吗?通常情况下,POJO 上的哈希码和相等性只需要为与 Maps 或 Sets 一起使用而实现。在 Maps 的情况下,通常会使用 ID 作为键,而不是 Pojo 本身。所以,您是否正在给自己增加工作量呢?

第二段加一分,但是最好一开始就以最小的努力把它做对,而不是在代码中添加hC/eq时再去挖掘。 - Ondra Žižka
就像所有事情一样,最终都是权衡取舍。如果一开始就完成它会很好,但根据我的经验,在实际需要时实现它并不过分困难。当然,我通常有测试来告诉我何时错过了实现它的机会。 :) - rfeak

0

记录

Java 16 增加了一个新功能,记录

如果你的类的主要目的是透明且不可变地传达数据,则将该类编写为记录。

默认情况下,您只需要声明成员字段即可。编译器会隐式创建构造函数、getter方法、equalshashCode 方法以及 toString 方法。

record Person ( String givenName , String surname ) {}

record Point ( int x , int y ) {}

record User ( UUID id, String name ) {}

记录的equalshashCode的默认行为是考虑每个成员字段。您可以覆盖以提供自己的逻辑。

我强烈建议阅读上面链接的Java JEP。您将了解到记录功能的目的不是减少样板代码。更少的代码是一个很好的额外好处。但是,如果您仅选择record来减少输入,您可能会滥用该功能。这种滥用可能会使阅读您的代码的人感到困惑,并可能导致其他问题。


不错。看起来很像 Kotlin 的 data class - Ondra Žižka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接