1. 程式人生 > >如何正確改寫equals方法

如何正確改寫equals方法

繼承關係下的equals改寫一文中,我簡單介紹了Martin Odersky提出的canEqual方案,此方案可以正確改寫基於繼承關係下的equals方法。本文結合Effective Java一書中提到的相關描述並借鑑canEqual方法,重新整理出一套行之有效的改寫equals的方案。敬請指正!

實現 canEqual的注意事項

首先需要指出的是,新改寫方案主要依賴於canEqual方法,canEqual要完成的任務是:

判斷當前物件(this)可以與哪些型別的目標物件進行內容上的比較

實現canEqual的方式比較自由,只要能完成上述任務即可。但在實現過程中需要注意:

1. canEqual的宣告

canEqual方法的正確宣告是:

public boolean canEqual(Object other);

其中引數 other就是目標物件。

2. canEqual方法不能丟擲任何異常。

這一要點還可以解釋為:使用instanceof而不是getClass方法來檢查實參的型別。原因在於實參為null的場合下instanceof可以返回false,而 getClass方法將會丟擲NullPointException。如使用instanceof的實現:

使用getClass方法的實現:

3. 確保例項化目標物件的類也存在canEqual方法

通常情況下,內容的比較來自於同一種類型的兩個物件之間;特殊情況下可以看到來自於具有繼承關係的兩類物件之間的內容比較。針對於這兩種情況,可以使用instanceof關鍵字實現 canEqual方法。這樣做同樣可以確保例項化目標物件所用的類一定存在canEqual方法。
但如果我們使用其它方式實現canEqual方法的話,請認真檢查例項化目標物件的類是否存在canEqual方法。

改寫equals的步驟

  1. 使用==操作符檢查“實參是否為指向物件的一個引用”。如果是的話,則返回true。
  2. 使用當前物件(this)的canEqual方法檢查是否可以與目標物件(實參)進行比較。如果不是的話,則返回false。
  3. 使用instanceof操作符(注1)檢查“實參是否為正確的型別”。如果“正確”的型別不止一個,按優先順序(注2)依次檢查每一個“正確”的型別。如果所有的“正確”型別都不是,則返回false。
  4. 針對每一個符合要求的“正確”型別,做以下處理:
    1. 把實參轉換到“正確”的型別。因為前面已經有了instanceof的檢查,所以這個轉換可確保成功。
    2. 使用目標物件(轉換型別之後的實參)的canEqual方法檢查是否可以與當前物件(this)進行比較。如果不是的話,則返回false(注3)
    3. 對於該類中每一個“關鍵”域,檢查實參中的域與當前物件中對應的域值是否匹配。檢查完畢將結果直接返回(注3)
  5. 當你改寫equals方法之後,問自己一個問題:equals處理的“正確”型別是否與canEqual方法中允許的“正確”型別等價(注4)

改寫equals的注意事項

1. 使用instanceof而不是getClass方法來檢查實參的型別。

原因參考實現canEqual的注意事項。

2. 正確處理多個“正確”型別的檢查優先順序。

當一個物件可以與多種型別的物件進行內容的比較時,型別檢查的先後順序可以根據業務的要求決定。通常情況下首先應該檢查是否與自身型別相符合,其次檢查其它型別。

3. 對於優先順序高的正確型別,優先返回匹配結果。

在改寫ColorPointEx的equals時(關於ColorPointEx的描述,請參考繼承關係下的equals改寫一文),正確型別有兩個:繼承自ColorPointEx的類與繼承自Point的類,並且ColorPointEx的檢查優先順序高於Point。當一個目標物件在優先順序高的型別(如ColorPointEx型別)比較下出現關鍵域內容不匹配的情況時,是否還需要進行下一個正確型別(如Point型別)的內容比較呢?回答是不需要。如下例:

此處我們應該不會再對兩個物件進行Point型別的內容比較了。

4. equals中處理的“正確”型別必須與canEqual中允許的“正確”型別等價。

以ColorPointEx為例,如果改寫的canEqual中允許的“正確”型別為繼承自Point的所有類,但改寫的equals中只處理了繼承自ColorPoint的所有類這一種“正確”的型別,這種改寫將會違反對稱性原則。

附:Point與ColorPointEx的最終實現

作為單一型別比較的例子,正確改寫Point的equals方法為:

作為多型別比較的例子,正確改寫ColorPointEx的equals方法為: