对于所有对象都通用的方法
Object类中所有的非final方法,都是可以被覆盖的,但是覆盖时要遵守一定的约定,如果不遵守约定,其他依赖于这些约定的类(例如HashMap和HashSet)就无法与该类一起正常运作。
第8条:覆盖equals时请遵守通用约定
如果类的每个实例都只与它自身相等,这种情况下就可以不覆盖equals方法。如果满足以下任何一个条件,就可以不用覆盖equals方法:
- 类的每个实例本质上都是唯一的。 对于代表活动实体而不是值(value)的类来说确实如此,例如Thread。Object提供的equals实现对于这些类来说正是正确的行为。
- 不关心类是否提供了“逻辑相等”的测试功能。 例如java.util.Random覆盖了equlas,以检查两个Random实例是否产生相同的随机数序列,但是设计者并不认为客户需要或者期望这样的功能,这样的情况下,从Object继承得到的equals时间已经足够了
- 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。 例如,大多数的Set实现都从AbstractSet继承equals实现,List实现从AbstractList继承equals实现,Map实现从AbstractMap继承equals实现。
- 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。 这这种情况下。无疑是应该覆盖equals方法的,以防它被意外调用:
@Override
public boolean equals(Object o) {
throw new AssertionError()://Method is never called
}
以上是不需要覆盖equals方法的情况,那是什么时候应该覆盖呢?如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法。这通常属于“值类”的情形。值类仅仅是一个表示值的类,例如Integer或者Date。程序员再利用equals方法来比较值对象的引用时,希望指定它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。覆盖了equals的类可以作为map的key,也可以作为set的元素,这样可以用map的get方法以及判断集合中是否存在该对象,如果未覆盖,equals比较的是地址。
有一种值类不需要覆盖equals方法,也就是单例类,每个类只存在一个实例。
覆盖equals时需要遵守一些通用约定:
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和y对象没有发生任何改变,无论一次还是多次调用x.equals(y)都应返回true
- 对于任何非null的引用值x,x.equasl(null)必须返回false