如果類需要有“邏輯相等”概念(不同於對象等同的概念),且其父類中也沒有實現同邏輯的equals()
方法時,譬如JDK8
中可以用自定義的類作爲Map
的key
,這時我們可以將類覆蓋Object.equals
方法。
通用約定
重寫equals
方法,需要遵循以下幾個約定:
1. 自反性
對於任何非null
的引用值x
,x.equals(x)
必須爲true
2. 對稱性
對於任何非null
的引用值x
和y
,當且僅當y.equals(x)
返回true
時,x.equals(y)
也必須返回爲true
3. 傳遞性
對於任何非null
的引用值x
、y
、z
,若x.equals(y)
爲true
,且y.equals(z)
爲true
,則x.equals(z)
也必爲true
4. 一致性
對於任何非null
的引用值x
和y
,只要equals
比較操作在對象中所用的信息沒有被修改,多次調用x.equals(y)
總會一直返回true
或者false
5. 對任何非null
的引用值x
,x.equals(null)
必須返回false
怎樣寫equals
方法
- 採用
==
來判斷兩個對象是否爲同個對象,是返回true
,否則false
- 採用
instanceof
來判斷參數是否具有正確的類型,否返回false
- 將參數強轉爲正確的類型
- 將類中每個有意義的屬性與參數中對應的屬性進行比較,相同則返回
true
,否則反之
1). 針對基本類型屬性(除了float
和double
),直接採用==
判斷
2). 針對引用類型,需要採用equals
進行判斷。如果是自定義的類作爲屬性,該自定義的類也必須重寫equals
方法
3). 針對float
屬性,採用Float.compare
判斷
4). 針對double
屬性,採用Double.compare
判斷
5). 針對數組屬性,需要將每個元素按以上原則逐一比較。若每個元素均爲很重要,可以採用Arrays.equals
方法
6). 針對某個屬性可以爲空的情形,需要指出防止拋出NullPointerException
異常,如(field == o.field || (field != null && field.equals(o.field)))
寫完後,最好按照通用約定中的5條約定進行測試。
示例代碼:
public final class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max)
throw new IllegalArgumentException(name +": " + arg);
}
@Override public boolean equals(Object o) {
//==判斷
if (o == this)
return true;
//instanceof判斷
if (!(o instanceof PhoneNumber))
return false;
//各屬性判斷
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
}
public int hashCode() {
//省略
}
}
注意點
- 重寫
equals
方法,需要重寫hashCode
方法 - 千萬別寫錯了
equals
方法,原方法是public boolean equals(Object obj)
,參數是Object
類型,非指定的類型