Skip to content

对所有对象都通用的方法

1. 覆盖 equals 请遵守通用规定

Object 提供的 equals() 方法通常是足够的,不要去覆写它,除非迫不得已。

覆盖 equals 的通用规定

如果类具有自己特有的 "逻辑相等" 概念,而且超类也没有覆盖 equals() 方法,那么这个类需要覆盖 equals() 方法。

在覆盖 equals() 方法时,(对于任何非空引用)需要遵守以下通用规定:

规范解释
自反性x.equals(x) 必须返回 true
对称性x.equals(y)true 时, y.equals(x) 也必须返回 true
传递性x.equals(y)true, y.equals(z)true 时, x.equals(z) 也必须返回 true
一致性只要 xy 没有改变,x.equals(y) 的返回值永远不会改变

实现高质量 equals 方法的诀窍

  1. 使用 == 操作符检查 “参数是否为这个对象的引用”。如果是,直接返回 true
  2. 使用 instanceof 操作符检查 “参数是否为正确的类型”
  3. 把参数转换成正确的类型
  4. 进行比较关键域时,优先比较最有可能不一致的域
  5. 覆盖 equals() 方法时,总要覆盖 hashCode() 方法
  6. 不要企图让 equals() 方法过于智能(会更复杂更难满足规约)
  7. 不要将 equals() 中声明的 Object 对象替换为其他类型
java
//这个方法不能正常工作,因为没有覆盖 `Object` 的 `equals()` 方法
public boolean equals(MyClass obj) { ... }
关键域类型比较方式
floatFloat.compare(float,float) 进行比较
doubleDouble.compare(double,double) 进行比较
其他基本类型使用 == 操作符进行比较
引用类型递归调用 equals() 方法进行比较

总结:不要轻易覆写 equals ; 如果需要,使用 Auto Value 框架或 IDE 生成的也优先于自己实现

2. 覆盖 equals 时总要覆盖 hashCode

Object 规范中要求,如果两个对象(equals)相等,那么它们的 hashCode() 方法也必须返回相同的值。

如果覆盖了 equals 方法导致两个实例对象逻辑相等,但这两个实例对象的原始 hashCode 肯定是不相等的,所以需要重写 hashCode 方法。

同样的,建议使用 Auto Value 开源框架或 IDE 来重写 hashCode 方法。

3. 始终要覆盖 toString

Object 类提供的toString()实现往往不是用户期望看到的,尽量覆盖更易于使用和调试。

4. 谨慎地覆盖 clone

除了数组之外,复制功能最好由构造器或工厂方法提供,而不是实现 Cloneable 接口的 clone() 方法

5. 考虑实现 Comparable 接口

compareTo() 方法并没有在 Object 类中声明,相反它是 Comparable 接口的唯一方法。

一旦类实现了 Comparable 接口,它就可以与许多泛型算法以及依赖于该接口的集合实现进行协作;事实上, jdk 的所有值类都实现了 Comparable 接口。

如果你正在编写一个值类,它具有非常明显的内在排序关系,那么就应该坚决考虑实现 Comparable 接口。

为 PhoneNumber 实现 Comparable 接口

java
final class PhoneNumber implements Comparable<PhoneNumber>{

    private final short areaCode, prefix, lineNum;

    public PhoneNumber(int areaCode, int prefix, int lineNum) {
        this.areaCode = rangeCheck(areaCode, 999, "area code");
        this.prefix = rangeCheck(prefix, 999, "prefix");
        this.lineNum = rangeCheck(lineNum, 9999, "line num");
    }

    private static short rangeCheck(int val, int max, String arg) {
        if (val < 0 || val > max)
            throw new IllegalArgumentException(arg + ": " + val);
        return (short) val;
    }
    
    //编写一个比较器
    private static final Comparator<PhoneNumber> COMPARATOR =
            //此次需要显示指定类型
            Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode)
                    .thenComparing(pn -> pn.prefix)
                    .thenComparing(pn -> pn.lineNum);

    @Override
    public int compareTo(PhoneNumber pn) {
        return COMPARATOR.compare(this,pn);
    }
}