第8條:覆蓋equals時請遵守通用約定
1. 前言
覆蓋equals方法看似很簡單,但是有許多覆蓋方法或導致錯誤,避免這些錯誤最直接的方法就是不覆蓋equals,這樣,沒有對象就只與自己相等。
2. 不需要覆蓋equals方法的情況有哪些?
(1)類的每一個實體本質上都是唯一的。比如Thread類,每一個實體都是唯一的,所以不需要覆蓋equals方法。
(2)不關心類是否提供了“邏輯相等”的測試功能。·
(3)父類已經覆蓋了equals方法,并且在子類中這些方法同樣適用。
(4)類是私有的或是包級私有的,可以確定equals方法永遠不會被調用。其實這種情況我們還是應該覆蓋equals的方法的,將equals方法設置成不可訪問的。像下面這樣:
@Override
public boolean equals(Object o) {
throw new AssertionError();
}
3. 需要覆蓋equals方法的情況
一般來說,“值類”都是需要覆蓋equals方法的。
什么類屬于“值類”?
對于那些只關心內容,不關心是否指向同一片內存空間的類,都屬于“值類”。
注意:有一種“值類”不需要覆蓋equals方法,那就是單例。也就是說,如果一個類,它又是單例又是“值類”的話,它就不需要覆蓋equals方法。
4. 在覆蓋equals方法時我們需要遵行的規范
(1)自反性:
即對于一個非空對象x,x.equals(x)必須返回true。
(2)對稱性:
即對于非空的對象x、y,如果x.equals(y)返回true的話,則y.equals(x)則必須返回true。
(3)傳遞性:
即對于非空的對象x、y、z,如果x.equals(y)返回true,y.equals(z)返回true,則x.eqauls(z)則必須返回true。
(4)一致性:
即對于非空對象x,y,只要equals方法的中比較的字段沒有被修改,那么x.equals(y)不管執行多少次都是返回true,或都是返回false。
(5)和null相比必須返回false:
即對于非空對象x,x.equals(null)必須返回false;
5. 實現高質量equals方法的訣竅
(1)用==操作符來檢查“參數是否是這個對象的引用”。如果是,則返回true。
這樣做是為了提高性能。
(2)使用instanceof操作符來檢查“參數是否為真確類型”。如果不是,則返回false。
“正確類型”一般指的是equals方法所在的類。有些情況是指該類所實現的某個接口。
(3)把參數轉化為真確的類型。
在被instanceof檢測過后,我們就將Object強行轉換成上面所提到的“正確的類型”,instanceof保證了我們轉換的真確性。
(4)對于該類中的每個關鍵域(字段),檢查參數中的域是否與該對象中所對應的域想匹配。
強轉之后,就可以開始匹配兩個對象中的字段了。如果匹配成功就可以返回true,如果“正確的類型”是一個接口,那么需要接口提供方法來訪問這些字段,如果是個類的話,那就不用說了。
字段匹配技巧:
a、如果是非float和double類型的基本數據類型,那么直接使用==符號。
b、如果是float和double,則使用Float.compare和Double.compare方法。
c、其他類型(也就是那些需要new出對象的類)則調用他們自身的equals方法。有些字段可能被允許為空,所以要進行判斷,如下:
field == null ? o.field == null : filed.equal(0.field);
d、數組的話需要遍歷每一個元素進行匹配,匹配的時候參考上面的三條方法。
6. 覆蓋equals方法的時候我們還需要注意的地方
(1) 覆蓋equals的時候總是要覆蓋hashCode方法。(為什么覆蓋和怎么覆蓋會在下一篇文章講)
(2) 不要企圖讓equals方法過于智能。只是匹配對象的類型和對象中的各個參數的話很容易做到,并且一般不會違反上面提到的規范。如果你過度的去尋求各種等價關系,那么上面的約定將很難遵守。
(4)覆蓋equals方法的時候請在方法前面加@Override
注解。
本文到此結束