首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >等于和hashCode与EqualsVerifier的合同

等于和hashCode与EqualsVerifier的合同
EN

Stack Overflow用户
提问于 2013-11-12 10:19:22
回答 2查看 6.3K关注 0票数 8

我对使用equals库的Java中的hashCodeEqualsVerifier契约有一些疑问。

想象一下我们有这样的东西

代码语言:javascript
复制
public abstract class Person {

    protected String name;

    @Override
    public boolean equals(Object obj) {
        // only name is taken into account
    }

    @Override
    public int hashCode() {
        // only name is taken into account
    }

}

以及以下扩展类:

代码语言:javascript
复制
public final class Worker extends Person {

    private String workDescription;

    @Override
    public final boolean equals(Object obj) {
        // name and workDescription are taken into account
    }

    @Override
    public final int hashCode() {
        // name and workDescription are taken into account
    }

}

我尝试使用equalsPerson类中测试是否履行了EqualsVerifier和契约。

代码语言:javascript
复制
    @Test
    public void testEqualsAndHashCodeContract() {
        EqualsVerifier.forClass(Person.class).verify();
    }

在运行这个测试时,我必须声明equalshashCode方法为final,但这是我不想做的事情,因为我可能希望在扩展类中声明这两个方法,因为我想在equalshashCode中使用一些子属性。

您可以跳过测试EqualsVerifier库中的最终规则吗?还是我漏掉了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-12-01 21:16:36

免责声明:我是EqualsVerifier的创造者。我只是发现了这个问题:)。

Joachim Sauer提到的解决办法是正确的。

让我解释一下为什么EqualsVerifier不喜欢您的实现。现在让我们假设Person不是抽象的;它使示例变得更简单一些。假设我们有两个Person对象,如下所示:

代码语言:javascript
复制
Person person1 = new Person("John");
Person person2 = new Worker("John", "CEO of the world");

让我们对这两个对象调用equals

代码语言:javascript
复制
boolean b1 = person1.equals(person2); // returns true
boolean b2 = person2.equals(person1); // returns false

b1为true,因为Personequals方法被调用,并且它忽略了workDescriptionb2是false,因为Workerequals方法被调用,而在该方法中的instanceofgetClass()检查返回false。

换句话说,根据equals,您的equals方法不再是对称的,这是正确实现equals的必要条件。

您确实可以使用getClass()来解决这个问题,但随后会遇到另一个问题。假设您使用Hibernate或模拟框架。这些框架使用字节码操作来创建类的子类。本质上,您将得到这样一个类:

代码语言:javascript
复制
class Person$Proxy extends Person { }

因此,假设您往返于数据库,如下所示:

代码语言:javascript
复制
Person person1 = new Person("John");
em.persist(person1);
// ...
Person fetchedPerson = em.find(Person.class, "John");

现在让我们打电话给equals

代码语言:javascript
复制
boolean b3 = person1.equals(fetchedPerson); // returns false
boolean b4 = fetchedPerson.equals(person1); // also returns false

b3b4是错误的,因为person1fetchedPerson属于不同的类(确切地说,是PersonPerson$Proxy )。equals现在是对称的,所以至少它遵循契约,但它仍然不是您想要的:fetchedPerson不再像Person那样“表现”。从技术上讲:这打破了Liskov代换原理,因为它是面向对象编程的基础。

有办法使所有这些工作,但它是相当复杂的。(如果你真的想知道:这篇文章解释了怎么做的。)为了保持简单,EqualsVerifier建议您将equalshashCode方法确定为最终方法。在大多数情况下,这会很好。如果你真的需要的话,你总是可以走复杂的路线。

在您的示例中,由于Person是抽象的,所以您也可以选择在Person中不实现equals,而只能在Worker (和可能拥有的任何其他子类)中实现。

票数 17
EN

Stack Overflow用户

发布于 2013-11-12 10:23:06

做得对是很棘手的。

EqualsVerifier文档解释了一个解决办法:

代码语言:javascript
复制
EqualsVerifier.forClass(MyClass.class)
    .withRedefinedSubclass(SomeSubclass.class)
    .verify();

请注意,要使此操作正常,您可能需要在等于中签入getClass(),因为Worker可能(或应该)永远不等于Person

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19926486

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档