【Effective Java】重写equals方法时的注意点

本文为《Effective Java》的读书笔记,经典的书每次读都会有不一样的感受,有些知识点只有有了相应的工作经验才能真正理解。相信大家刚开始学须 Java 基础时就对 equals 方法进行了重点学习。这里再总结下注意点。

Equals 方法的通用约定

  • 自反性(reflexive):对于任何非 null 的引用值 x, x.equals(x) 必须返回 true
  • 对称性(symmetric):对于任何非 null 的引用值 x 和 y,当且仅当 y.equals(x) 时返回true,x.equals(y) 也必须返回 true
  • 传递性(transitive):对于任何非 null 的引用值 x, y, z 如果 x.equals(y) = true, y.equals(z) = true ,那么 x.equals(z) = true
  • 一致性(consistent):对于任何非 null 的引用值 x 和 y,只要equals 的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 就会一致地返回 true,或者一致地返回 false。
  • 非空性(Non-nullity):o.equals(null) = false

违反通用约定的案例

违反对称性:

// Broken - violates symmetry!  (Page 39)
public final class CaseInsensitiveString {
    private final String s;

    public CaseInsensitiveString(String s) {
        this.s = Objects.requireNonNull(s);
    }

    // Broken - violates symmetry!
    @Override public boolean equals(Object o) {
        if (o instanceof CaseInsensitiveString)
            return s.equalsIgnoreCase(
                    ((CaseInsensitiveString) o).s);
        if (o instanceof String)  // One-way interoperability!
            return s.equalsIgnoreCase((String) o);
        return false;
    }
}

违反传递性:

public class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override public boolean equals(Object o) {
        if (!(o instanceof Point))
            return false;
        Point p = (Point)o;
        return p.x == x && p.y == y;
    }
}

public class ColorPoint extends Point {
    private final Color color;

    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }

    // Broken - violates symmetry!  (Page 41)
    @Override public boolean equals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        return super.equals(o) && ((ColorPoint) o).color == color;
    }
        public static void main(String[] args) {
        // First equals function violates symmetry (Page 42)
        Point p = new Point(1, 2);
        ColorPoint cp = new ColorPoint(1, 2, Color.RED);
        System.out.println(p.equals(cp) + " " + cp.equals(p));

        // Second equals function violates transitivity (Page 42)
        ColorPoint p1 = new ColorPoint(1, 2, Color.RED);
        Point p2 = new Point(1, 2);
        ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE);
        System.out.printf("%s %s %s%n",
                          p1.equals(p2), p2.equals(p3), p1.equals(p3));
    }
}

上述代码输出结果为 true, true, false,可以很明显地看到破坏了传递原则。
里氏替换原则认为:一个类型的任何重要属性也将适用于它的子类型,因此为改类型编写的任何方法,在他的子类型傻姑娘也应该同样运行地很好。针对上述的问题:我们遵从 Favor composition over inheritance(复合优于继承)的原则,ColorPoint 不再继承于 Point, 而是将 point 作为 ColorPoint 的属性.

// Adds a value component without violating the equals contract (page 44)
public class ColorPoint {
    private final Point point;
    private final Color color;

    public ColorPoint(int x, int y, Color color) {
        point = new Point(x, y);
        this.color = Objects.requireNonNull(color);
    }

    /**
     * Returns the point-view of this color point.
     */
    public Point asPoint() {
        return point;
    }

    @Override public boolean equals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        ColorPoint cp = (ColorPoint) o;
        return cp.point.equals(point) && cp.color.equals(color);
    }

    @Override public int hashCode() {
        return 31 * point.hashCode() + color.hashCode();
    }
}

注意,可以在一个抽象类的子类中增加新的值组件且不违反 equals 约定,因为我们不能创建抽象类的实例。

Equals方法注意点

  • 可以使用 Float.compare(float, float), Double.compare(double, double) 来进行比较小数,还可以使用 Arrays.equals() 来比较数组。 Java 7中,所有基本类型的包装类都增加了 compare 方法,推荐使用其进行比较。
  • 可以使用 google 的 AutoValue 框架自动生成 equals 代码

覆盖 equals 时必须覆盖 hashCode

hashCode 的通用约定:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.
    其中第二条在Map中尤其重要,因为在 map 的 get 和 put 方法中会优先使用到 hashCode 进行比较.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值