Effective Java Item 10 - 覆寫equals方法時必須遵守一般契約
整理Effective Java書中Item 10: Obey the general contract when overriding equals心得筆記
主旨
當覆寫 equals()
方法時,必須遵守其一般契約,這是設計 equals()
方法時的基本要求。如果不遵守這些規範,會導致難以預測的錯誤。
劃重點
equals()
方法的一般契約
- 對稱性:如果
a.equals(b)
返回true
,那麼b.equals(a)
也應該返回true
。 - 自反性:對於任何非
null
的參照變數a
,a.equals(a)
必須返回true
。 - 傳遞性:如果
a.equals(b)
返回true
且b.equals(c)
返回true
,那麼a.equals(c)
必須返回true
。 - 一致性:如果兩個物件相等,多次調用
a.equals(b)
應該返回相同的結果,前提是b
沒有改變。 - 對
null
的比較:a.equals(null)
應該返回false
。
為什麼要遵守這些規則?
- 一致性和預測性:如果不遵守
equals()
的契約,可能會導致程序行為異常,進而破壞集合的行為,尤其是在使用如HashSet
或HashMap
這類基於哈希值的容器時,會出現意料之外的結果。 - 容器的正確性:不遵守契約可能會讓集合類型無法正確識別相等物件,從而造成錯誤的去重或插入。
實際範例:正確覆寫 equals()
方法
說明
在這個範例中,equals()
方法遵循了上述所有契約。使用 username
和 age
作為兩個物件相等的標準,確保物件的相等性是基於其重要的屬性進行比較。
覆寫 hashCode()
方法是必要的,因為如果兩個物件相等(即 equals()
返回 true
),它們的哈希值也必須相同。這是 HashSet
和 HashMap
正確工作的基礎。若 hashCode()
方法與 equals()
方法不一致,可能會導致容器中的錯誤行為,如無法正確查找或去重相等的物件。
常見錯誤:不遵守 equals()
的契約
以下是一個錯誤範例,這種做法會導致無法正確識別相等的物件,尤其在容器中使用時會出現問題。
問題:
這個 equals()
方法沒有比對 age
屬性,導致相同 username
但不同 age
的兩個物件被認為是相等的。
同樣,hashCode()
也未能遵守契約,僅根據 username
計算哈希值,這將導致哈希衝突,影響如 HashSet
和 HashMap
這類基於哈希值的容器的正確性。
小結
覆寫 equals()
方法時,一定要遵守其一般契約,確保方法正確運行,並避免容器中的錯誤行為。
同時,記得覆寫 hashCode()
方法,以確保它與 equals()
一致,保持 HashSet
和 HashMap
的正確性。
Read other posts