Java 對象 覆蓋equals時請遵守通用約定
如果滿足了以下任何一個條件,就是所期望的結果:
類的每個實例本質上都是唯一的。
不關心類是否提供了“邏輯相等”的測試功能。
超類已經覆蓋了equals,從超類繼承過來的行為對於子類也是合適的。
類是私有的或是包級私有的,可以確定它的equals方法永遠不會被調用。
如果類具有自己特有的“邏輯相等”概念(不同於對象等同的概念),而且超類還沒有覆蓋equals以實現期望的行為,這時我們就需要覆蓋equals方法。這通常屬於“值類”的情形。值類僅僅是一個表示值的類,例如Integer或者Date。在用equals方法比較值對象的引用時,希望知道它們在邏輯上是否相等,而不是想了解它們是否指向同一個對象。
確保“每個值至多只存在一個對象”的類不需要覆蓋equals方法。對於這樣的類而言,邏輯相同與對象等同是一回事,Object的equals方法等同於邏輯意義上的equals方法。
equals方法實現了等價關系:
自反性:對於任何非null的引用值x,x.equals(x)必須返回true。
對稱性:對於任何非null的引用值x和y,僅當y.equals(x)返回true時,x.equals(y)必須返回true。
傳遞性:對於任何非null的引用值x、y和z,如果x.equals(y)返回true,並且y.equals(z)也返回true,那麽x.equals(z)也必須返回true。
一致性:對於任何非null的引用值x和y,只要equals的比較操作在對象中所用的信息沒有被修改,多次調用x.equals(y)就會一致地返回true,或者一致地返回false。
對於任何非null的引用值x,x.equals(null)必須返回false。
無法在擴展可實例化的類的同時,既增加新的值組件,同時又保留equals約定。在一個抽象類的子類中增加新的值組件,不會違反equals約定,因為不能創建抽象類的實例。
如果instanceof的第一個操作數為null,那麽,不管第二個操作數是哪種類型,instanceof操作符都會返回false。
實現高質量equals方法的訣竅:
使用==操作符檢查“參數是否為這個對象的引用”。
使用instanceof操作符檢查“參數是否為正確的類型”。
把參數轉換成正確的類型。
對於該類中的每個“關鍵”域,檢查參數中的域是否與該對象中對應的域相匹配。
對於既不是float也不是double類型的基本類型域,可以使用==操作符進行比較;對於對象引用域,可以遞歸地調用equals方法;對於float域,可以使用Float.compare方法;對於double域,則使用Double.compare方法。對float和double域進行特殊的處理,是因為存在Float.NaN、-0.0f以及類似的double常量。對於數組域,則要把以上這些規則應用到每個元素上。如果數組域中的每個元素都很重要,就可以使用發行版本1.5中新增的其中一個Arrays.equals方法。
最後的建議:
覆蓋equals時總要覆蓋hashCode。
不要企圖讓equals方法過於智能。
不要將equals方法聲明中的參數Object對象替換為其他的類型。因為替換之後方法不是重寫,而是重載。
參考資料
《Effective Java 中文版 第2版》 第8條:覆蓋equals時請遵守通用約定 P28-38
Java 對象 覆蓋equals時請遵守通用約定