当前位置:数据分析 > java - 重写equals方法并没有想象中那么简单

java - 重写equals方法并没有想象中那么简单

  • 发布:2023-10-10 21:51

最近和同事聊了equals和==的区别。这其实是一个很古老很简单的问题,但是当你想自己重写equals方法的时候,你发现有些东西你不知道却又不得不知道。覆盖等于需要大量的关注。虽然Object是一个非常具体的类,但它的主要作用是扩展。他所有的非最终方法都有明确的通用约定。因为它们被设计为被重写的方法。任何重写 equals、hashCode、toString、clone 和 Finalize 的类都有责任遵守这些方法的通用约定。如果做不到这一点,多个类组合时就很难达到预期的效果。

不覆盖 equals 方法


重写 equals 方法看似简单,但有很多重写方法可能会导致错误。避免此错误的最简单方法是不重写 equals,在这种情况下,每个类实例仅等于其自身。那么什么情况下我们可以选择不重写equals方法呢?

类的每个实例本质上都是唯一的

对于表示活动实体而不是值的类(例如每个线程实例)来说,这是正确的。我们将它与 equals 方法进行比较是没有意义的,因为每个线程都是唯一的。在这种情况下,我们不需要重写equals方法,因为Object类中的equals已经完全足够了。

Object 类中 equals 方法的实现:
public boolean equals(Object obj) { return (this == obj); }

不关心类是否需要逻辑相等判断

有些类是“数字类”。比较大小和数学运算是这些类的工作。这种情况下,我们需要比较类中存储的值,并做出逻辑相等判断。除了类之外,大多数类都没有“是否相等”的概念。此类不关心逻辑相等的类不需要重写 equals 方法。

超类实现的 equals 也适用于子类

比如继承AbstractSet类的HashSet类的equals方法没有区别,所以HashSet可以直接使用AbstractSet的equals方法。

覆盖等于方法

与上面相反,我们需要重写equals方法的情况是:如果类有自己的逻辑相等概念,并且父​​类没有可用的equals方法重写。这个时候就需要我们自己去覆盖了。

集合中equals方法的等价关系


equals 方法实现了等价关系。我学习了离散数学中的等价关系的概念。对于R上的二元关系,如果满足自发对称性和传递性,则它是等价的。我们来详细分析一下 equals 和这三个属性的关系。

自反性:对于任何非空参考值 x,x.equals(x) 必须返回 true

对称性:对于任何非空参考值x和y,x.equals(y)必须返回true当且仅当y.equals(x)返回true

传递性:对于任何非空引用值x、y和z,如果x.equals(y)返回true并且y.equals(z)也返回true,则x.equals(z ) 必须返回 true。

自反性: ∀ a εA, => (a, a) ε R
对称性: (a, b) εR∧ a ≠ b => (b, a)εR
传递性: ( a, b) ∈ R, (b, c) ∈ R => (a, c) ∈ R

比较这三个属性,没有问题。可见equals方法实现了等价关系。

equals方法怎么写


Object的equals方法只是简单的看地址,这显然无法满足我们的要求。那么我们自己写equals方法的时候如何才能保证写出一个高质量的、符合逻辑的比较方法呢? equals的写法可以概括为以下四个步骤:

1。使用 == 运算符检查参数是否只是对对象的引用
如果结果相等,则返回 true,说明 x 和 y 是对对象的不同引用,不再进一步判断需要。

2。使用instanceof运算符检查参数类型是否正确
如果结果类型不正确,则返回false。因为我们的equals方法继承自Object类,所以无法避免参数类型。 Object,我们首先使用instanceof来判断参数的类型。如果类型不正确,则无需进行下一步判断。

3。将参数转换为正确的类型
因为之前已经做过检测,所以这一步的类型转换没有问题。

4。判断每个类中需要逻辑比较的域值
保证x和y是同一类型的不同实例后,取出需要判断逻辑比较的域值,执行比较和判断。能。如果全部正确则返回 true,否则返回 false。

执行equals需要注意的事项


写完equals方法后,必须反复判断是否符合自反性、对称性和传递性。不仅如此,在保证相等的情况下编写equals方法还有一些值得注意的地方,我们需要对此进行改进。

重写 equals 方法时一定要重写 hashCode 方法
如果我们编写一个关于哈希的类,那么在重写 equals 方法时必须重写 hashCode 方法。因为在哈希表中,逻辑上相同的对象应该具有相同的哈希码。举一个比较简单的例子:将一个String存储在HashSet中。有可能两个内容相同的String字符串使用==判断为假,但HashSet中只存在一份。这是因为具有相同逻辑的字符串具有相同的hashCode。
一般来说,如果你为你的类重写了 equals 方法,就证明在某种情况下会有两个不同的对象在逻辑上相等。如果此时与哈希相关,那么两个对象需要相同的hashCode。因此,当重写equals方法时,一定要重写hashCode方法。

不要把equals方法搞得太聪明了
如果我们简单的按照上面的实现流程来写equals方法,是符合规定的,不会出现奇怪的错误。但如果一味追求各种花哨的等价关系,让代码变得臃肿,不仅违背了高内聚的初衷,还会导致代码出现一些莫名其妙的错误。

不要把equals方法的参数类型搞错了
说起来可能觉得好笑,但事实就是这样。修改参数类型后,equals方法就与Object类无关了。编译器不会报错,只给程序员留下无尽的头痛。如果您没有意识到参数类型是 Object,您可能会花几个小时想知道为什么您的程序无法正常运行。

相关文章

热门推荐