【Effective Java】條8:覆蓋equals時請遵守通用約定

如果類需要有“邏輯相等”概念(不同於對象等同的概念),且其父類中也沒有實現同邏輯的equals()方法時,譬如JDK8中可以用自定義的類作爲Mapkey,這時我們可以將類覆蓋Object.equals方法。

通用約定

重寫equals方法,需要遵循以下幾個約定:
1. 自反性
對於任何非null的引用值xx.equals(x)必須爲true
2. 對稱性
對於任何非null的引用值xy,當且僅當y.equals(x)返回true時,x.equals(y)也必須返回爲true
3. 傳遞性
對於任何非null的引用值xyz,若x.equals(y)true,且y.equals(z)true,則x.equals(z)也必爲true
4. 一致性
對於任何非null的引用值xy,只要equals比較操作在對象中所用的信息沒有被修改,多次調用x.equals(y)總會一直返回true或者false
5. 對任何非null的引用值xx.equals(null)必須返回false

怎樣寫equals方法

  1. 採用==來判斷兩個對象是否爲同個對象,是返回true,否則false
  2. 採用instanceof來判斷參數是否具有正確的類型,否返回false
  3. 將參數強轉爲正確的類型
  4. 將類中每個有意義的屬性與參數中對應的屬性進行比較,相同則返回true,否則反之
    1). 針對基本類型屬性(除了floatdouble),直接採用==判斷
    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() {
        //省略
    }
}

注意點

  1. 重寫equals方法,需要重寫hashCode方法
  2. 千萬別寫錯了equals方法,原方法是public boolean equals(Object obj),參數是Object類型,非指定的類型
發佈了98 篇原創文章 · 獲贊 180 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章