1. 程式人生 > >Java 對象 覆蓋equals時請遵守通用約定

Java 對象 覆蓋equals時請遵守通用約定

聲明 實例 等價關系 發行版本 擴展 sof 高質量 span 等價

  如果滿足了以下任何一個條件,就是所期望的結果:

  類的每個實例本質上都是唯一的。

  不關心類是否提供了“邏輯相等”的測試功能。

  超類已經覆蓋了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時請遵守通用約定