0%

Effective Java 第三版 (2) 所有对象都通用的方法

所有对象都通用的方法

§ 重写 equals 时请遵守通用约定

什么时候应该重写 Object.equals?

如果类具有自己特有的 “逻辑相等” 概念,父类还没有重写过 equals 以实现期望的行为,这时我们需要重写 equals 方法,这通常属于 “值类” 的情形。

值类是仅仅一个表示值的类,例如 Integer。或者自己定义的 DTO 类。

重写 equals 方法的通用约定:

  • 自反性。对于任何非 null 的引用 x,x.equals(x) 必须返回 true
  • 对称性。对于任何非 null 的引用 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 必须返回 true
  • 传递性。对于任何非 null 的引用 x、y、z,如果 x.equals(y) 返回 true,并且 y.equals(z) 也返回 true,那么 x.equals(z) 也必须返回true
  • 一致性。对于任何非 null 的引用 x 和 y,多次调用 x.equals(y) 会一致返回 true 或 false
  • 对于任何非 null 的引用 x,x.equals(null) 必须返回 false。

同时还有最重要的提醒:

  • 重写 equals 时总要重写 hashCode
  • 不要试图让 equals 方法过于智能
  • 不要将 equals 声明中的 Object 对象替换为其他类型。
1
2
3
4
// 这是错误的,没有使用 Object 变成了重载,而不是重写
public boolean equals(MyClass o) {
...
}

§ 重写 equals 时总要重写 hashCode

如果不这样做会违反 Object.hashCode 通用约定,导致该类无法结合所有基于散列的集合一起正常运作,包括 HashMapHashSet 等。

相等的对象必须具有相等的 散列码(hash code)

§ 始终要重写 toString

toString 通用约定指出,返回的字符串应该是:简洁的,信息丰富,且易于阅读的表达形式

§ 谨慎的重写 clone

§ 考虑实现 Comparable 接口

一旦类实现了 Comparable 接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作,付出很小的努力就可以获得非常强大的功能。

Java 平台中所有的值类都实现了 Comparable 接口。如果你正在编写一个值类,具有非常明显的内在排序关系,那么应该坚决考虑实现这个接口:

1
2
3
public interface Comparable<T> {
int compareTo(T t);
}

compareTo 方法的通用约定与 equals 方法的相似。