覆蓋equals時請遵守通用約定

第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注解。

本文到此結束

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容