【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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章