Effective Java Item 8 note
Obey the general contract when overriding equals
底下是可以不需要Override equals的條件,任一皆可:
- class的每個instance都是唯一的
- 不關心class是否提供logical equality的測試功能
- Super class已經Override equals,然後在子類也通用。
- Private class or private inner class,可以確定equals永遠不會被使用。
底下是需要Override equals的情形:
出現需要自己處理邏輯相等的定義,且super class也還沒有Override。Ex : value class。
Equals的通用約定:
要滿足equivalence relation:
- Reflexive : non null x, x.equals(x) 必須是true
正常應該是不會有問題。
- Symmetric:non null x,y , x.equals(y) 是true。那麼y.equals(x) 也要是true
書本上的例子是Wrapper primitive class,然後多判斷了原本的primitive的條件,但是primitive type並不知道這個wrapper class是誰造成的。
- Transitive:non null x,y,z , x.equals(y) 是true且y.equals(z)也是true。那麼y.equals(x) 也要是true
書本上的例子是,想像一下有個value component class,如果被extends之後,會需要怎麼處理這個equals
a. 使用parent的equals,那麼children的內容就不會被比較到了(X)
b. 如果override掉parent的equals,要嘛違反Symmetric,不然就是違反
Transitive
這是在面對oo上面關於等價關係的一個基本問題,我們沒辦法在擴展可實例化的class的同時,既增加新的值同時又保留equals。
而且當你使用到Collection的class,裡面的contains的method用的是equals去做存在的比較,所以這樣的處理也會造成錯誤的結果。
解法:
可以使用compose取代extends,讓看起來比較像has a的屬性自己成立一個class,而不是硬要使用is a,而去破壞了原本class的Transitive
P.S. 不要混用java.sql.Timestamp 跟java.util.Date,java.sql.Timestamp的equals破壞了對稱性。
- Consistent:non null x,y, 只要在equals的比較操作中所使用到的值沒有被修改,多次使用x.equals(y)應該都是返回一樣的結果
不要讓equals依賴於不可靠的資源,像是書本上提到的java.net.URL的equals是依賴URL中主機的ip,ip是可能隨著時間變動不同的。
- Non-nullity
對於non null x, x.equals(null)必須是false
可以利用if (!(o instances Type) ) return false;來判斷是不是對的class以及object是不是null
記得要寫unit test來測試你有override equals的class。
記得override equals也同時要override hashCode(item 9)
記得不要讓equals過於聰明
記得不要這樣寫
public boolean equals(MyClass o),這樣不是Override是overload