[英]Equals and hashCode contract with EqualsVerifier
我對使用EqualsVerifier庫的Java中的equals
和hashCode
契約有些懷疑。
想象一下,我們有類似的東西
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
}
}
以下擴展課程:
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
}
}
我嘗試使用EqualsVerifier來測試我是否在Person類中實現了equals
和hashCode
契約
@Test
public void testEqualsAndHashCodeContract() {
EqualsVerifier.forClass(Person.class).verify();
}
運行這個測試,我得到我必須聲明equals
和hashCode
方法final,但這是我不想做的事情,因為我可能想在擴展類中聲明這兩個方法,因為我想使用一些child的屬性在equals
和hashCode
。
你可以跳過測試EqualsVerifier庫中的最終規則嗎? 或者我錯過了什么?
免責聲明:我是EqualsVerifier的創建者。 我才發現這個問題:)。
Joachim Sauer提到的解決方法是正確的。
讓我解釋為什么EqualsVerifier不喜歡你的實現。 讓我們假裝現在Person
不是抽象的; 它使示例更簡單一些。 假設我們有兩個Person
對象,如下所示:
Person person1 = new Person("John");
Person person2 = new Worker("John", "CEO of the world");
讓我們在這兩個對象上調用equals
:
boolean b1 = person1.equals(person2); // returns true
boolean b2 = person2.equals(person1); // returns false
b1
是真的,因為調用了Person
的equals
方法,它忽略了workDescription
。 b2
為false,因為調用了Worker
的equals
方法,並且該方法中的instanceof
或getClass()
檢查返回false。
換句話說,根據Javadoc ,你的equals
方法不再是對稱的,這是正確實現equals
的要求。
你確實可以使用getClass()
來解決這個問題,但是你遇到了另一個問題。 假設您使用Hibernate或模擬框架。 這些框架使用字節碼操作來創建類的子類。 基本上,你會得到這樣一個類:
class Person$Proxy extends Person { }
所以假設您要往返數據庫,如下所示:
Person person1 = new Person("John");
em.persist(person1);
// ...
Person fetchedPerson = em.find(Person.class, "John");
現在,讓我們呼喚equals
:
boolean b3 = person1.equals(fetchedPerson); // returns false
boolean b4 = fetchedPerson.equals(person1); // also returns false
b3
和b4
是假的,因為person1
和fetchedPerson
屬於不同的類( fetchedPerson
是Person
和Person$Proxy
)。 equals
是對稱的,現在,所以至少要遵循合同,但它仍然不是你想要什么: fetchedPerson
不“行為”就像一個Person
了。 在技術術語中:這打破了Liskov替換原則 ,這是面向對象編程的基礎。
有一種方法可以完成所有這些工作,但它非常復雜。 (如果你真的想知道: 這篇文章解釋了如何。)為了簡單起見,EqualsVerifier建議你使你的equals
和hashCode
方法最終。 在大多數情況下,這將工作正常。 如果你真的需要,你可以隨時采取復雜的路線。
在您的情況下,由於Person
是抽象的,您也可以選擇不在Person
實現equals
,而只在Worker
(以及您可能擁有的任何其他子類)中實現equals
。
做到這一點非常棘手。
EqualsVerifier的文檔解釋了一種解決方法:
EqualsVerifier.forClass(MyClass.class)
.withRedefinedSubclass(SomeSubclass.class)
.verify();
請注意,要使其工作,您可能需要檢查equals中的getClass()
,因為Worker
可以(或應該)永遠不等於Person
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.