Effective Java 阅读笔记

第三章 对于所有对象都通用的方法

第 10 条 重写 equals 时请遵守通用约定

重写 equals 方法很简单,但是很容易出现错误,最直接避免这种错误的方式就是不重写 equals,
当出现任意一下情况的时候,就不需要重写 equals:

  • 类的每个实例在逻辑上就是唯一的
  • 没比要提供“逻辑相等”功能的
  • 父类的 equals 方法对子类也适用时
  • 私有类,并且确定 equals 方法不会被调用

上述任一情况就可以不重写 equals 方法。

如果需要提供 equals 方法,那应该遵循一下约定:

  • 自反性(reflexive):x 不为 null 时,x.equals(x)应当返回 true
  • 对称性(symmetric):x、y 不为 null 时,如果x.equals(y)为 true,那么y.equals(x)也必须为 true
  • 传递性(transitive):x、y、z 不为 null 时,如果x.equals(y) && y.equals(z)为 true,那么x.equals(z)也必须为 true
  • 一致性(consistent):如果x.equals(y),并且x、y的值都没有修改,那么x.equals(y)应始终为 true
  • 任何非 null 的引用x,x.equals(null)必须为 false

其他告诫:

  • 重写 equals 时同时重写 hashCode 方法
  • 不要让 equals 的实现过于繁重
  • 不要修改 equals 参数的类型,也就是不要修改 Object 为具体的类

第 11 条 重写 equals 时必须重写 hashCode 方法

如果不重写 hashCode 方法,会导致 HashMap、HashSet等类无法正常运作。

下面是 Object 的规范:

  • 如果对象没有改变,那么 HashCode对象的返回值应该是相同的
  • 如果两个对象使用 equals 比较时相等了,那么 hashCode 方法必须返回相同的值
  • 如果两个对象使用 equals 比较时不相等,那么 hashCode 方法返回值没有要求

第 12 条 尽量重写 toString 方法

可以方便 debug 等

第 13 条 谨慎重写 clone 方法

clone 方法在 Object 类上是 protected,实现了 Cloneable 接口的类并不一定具有能调用的 clone 方法.

Cloneable 接口只是标记该类可以被 clone,并且重写 clone 方法需要做的事情并不少,不仅需要逐一 clone 内部的引用类型,还需要调用 super.clone(),反正谨慎使用 clone 方法。

第 14 条 可比较对象可以实现 Comparable 接口

Comparable 接口中有一个返回值为 int 类型的单个方法:

public interface Comparable<T> {
    public int compareTo(T o);
}

当前对象大于、等于、小于被比较的对象时,分别返回正数、0、负数。

实现接口时,有几点需要注意的:
由于 compareTo 方法只看正负,不看具体值,因此这里使用Math.signum()来表示

  • 实现必须保证signum(x.compareTo(y)) == -signum(y.compareTo(x)),即 x 向 y 比较,和 y 向 x 比较的意义是相同的。
  • 比较关系必须可传递,即当x.compareTo(y) && y.compareTo(z)为 true 时,那么x.compareTo(z)也一定为 true
  • 比较关系必须等价,即当x.compareTo(y) == 0时,那么任意 z 有:signum(x.compareTo(z)) == signum(y.compareTo(z))
  • 强烈建议(x.compareTo(y) == 0) == (x.equals(y)),但是并非绝对必要,此时需要说明排序与 equals 不一致。

当违反前三条时,很有可能造成排序算法死循环(亲身经历)。

在 compareTo 方法中直接比较大于小于是非常繁琐的,可以使用 Java 8 新增的接口:Comparator

这里有一个比较的例子,根据实体类的字段进行比较:

@Getter
@Setter
@ToString
public static class MyEntity implements Comparable<MyEntity> {
    private static final Comparator<MyEntity> COMPARATOR = Comparator.nullsLast(
            Comparator.comparing(MyEntity::getName)
    );

    private String name;

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

使用 Comparator 静态方法构造一个 Comparator 接口进行比较,会更加方便、易读(需要注意 null)。

标签: Java

添加新评论