重写 equals 方法时遵守通用约定

 

重写 equals 方法时遵守通用约定

虽然 Object 是一个具体的类,但它主要是为继承而设计的。它的所有非 final 方法(equals、 hashCode、toString、clone 和 finalize)都有清晰的通用约定( general contracts),因为它们被设计为 被子类重写。任何类要重写这些方法时,都有义务去遵从它们的通用约定;如果不这样做,将会阻止其 他依赖于约定的类 (例如 HashMap 和 HashSet) 与此类一起正常工作

重写 equals 方法看起来很简单,但是有很多方式会导致重写出错,其结果可能是可怕的。避免此问 题的最简单方法是不覆盖 equals 方法,在这种情况下,类的每个实例只与自身相等。如果满足以下任一 下条件,则说明是正确的做法: 每个类的实例都是固有唯一的。 对于像 Thread 这样代表活动实体而不是值的类来说,这是正确 的。 Object 提供的 equals 实现对这些类完全是正确的行为。 类不需要提供一个「逻辑相等(logical equality)」的测试功能。例如 java.util.regex.Pattern 可以重写 equals 方法检查两个是否代表完全相同的正则表达式 Pattern 实例,但是设计者并不认为客户需要或希望使用此功能。在这种情况下,从 Object 继承的 equals 实现是最合适的。 父类已经重写了 equals 方法,则父类行为完全适合于该子类。例如,大多数 Set 从 AbstractSet 继 承了 equals 实现、List 从 AbstractList 继承了 equals 实现,Map 从 AbstractMap 的 Map 继承了 equals 实现。 类是私有的或包级私有的,可以确定它的 equals 方法永远不会被调用。如果你非常厌恶风险,可 以重写 equals 方法,以确保不会被意外调用:

 
 
 
x
 
 
 
 
@Override
public boolean equals(Object o) {
throw new AssertionError(); // Method is never called
}
 

什么时候需要重写 equals 方法呢?如果一个类包含一个逻辑相等(logical equality)的概念,此概 念有别于对象标识(object identity),而且父类还没有重写过 equals 方法。这通常用在值类(value classes)的情况。值类只是一个表示值的类,例如 Integer 或 String 类。程序员使用 equals 方法比较值 对象的引用,期望发现它们在逻辑上是否相等,而不是引用相同的对象。重写 equals 方法不仅可以满足 程序员的期望,它还支持重写过 equals 的实例作为 Map 的键(key),或者 Set 里的元素,以满足预期 和期望的行为。 一种不需要 equals 方法重写的值类是使用实例控制(instance control)(详见第 1 条)的类,以确 保每个值至多存在一个对象。 枚举类型(详见第 34 条)属于这个类别。 对于这些类,逻辑相等与对象 标识是一样的,所以 Object 的 equals 方法作用逻辑 equals 方法。 当你重写 equals 方法时,必须遵守它的通用约定。Object 的规范如下: equals 方法实现了一个等 价关系(equivalence relation)。它有以下这些属性:

自反性: 对于任何非空引用 x, x.equals(x) 必须返回 true。

对称性: 对于任何非空引用 x 和 y,如果且仅当 y.equals(x) 返回 true 时 x.equals(y) 必须返回 true。

传递性: 对于任何非空引用 x、y、z,如果 x.equals(y) 返回 true, y.equals(z) 返回true,则 x.equals(z) 必须返回 true。

一致性: 对于任何非空引用 x 和 y,如果在 equals 比较中使用的信息没有修改,则 x.equals(y) 的多次调用必须始终返回 true 或始终返回 false。 对于任何非空引用 x, x.equals(null) 必须返回 false

猜你喜欢

转载自www.cnblogs.com/lIllIll/p/12571993.html