【Effective Java 10.1】覆盖 equals 时请遵守通用约定
1. 不需要覆盖 equals 方法的情况
覆盖 equals
方法看起来很简单,但是有许多覆盖方式会导致错误,并且后果非常严重。最容易避免这类问题的办法就是不覆盖 equals 方法,在这种情况下,类的每个实例都只与其自身相等。以下情况不需要覆盖 equals 方法:
- 类的每个实例本质上都是唯一的:对于代表 “活动实体” 而非 “值” 类,例如
Thread
。 - 类没有必要提供 ”逻辑相等“ 的测试功能:例如,
java.util.regex.Pattern
可以覆盖equals
,以检查两个Pattern
实例是否代表同一个正确表达式,但是设计者并不认为客户需要或者期望这样的功能。在这类情况之下,从Object
继承得到了equals
实现已经足够了 - 超类已经覆盖了 equals,超类的行为对于这个类也是合适的。例如,大多数的
Set
实现都从AbstractSet
继承equals
实现,List
实现从AbstractList
继承equals
实现,Map
实现从AbstractMap
继承equals
实现。 - 类是私有的,或者是包级私有,可以确定它的 equals 方法永远不会被调用。
2. 需要覆盖 equals 方法的情况
对于大部分 ”值“ 类对象都需要覆盖 equals 方法。例如 Interger
或 String
。有时甚至需要同时覆写 hashCode
方法。但有一种值类不需要覆盖 equals 方法,即实例受控 ”确保对象只存在一个“(如单例对象,枚举类)
3. 覆盖 equals 方法时的约定
- 自反性(reflexive):对于任何非 null 的引用值。
x.equals(x)
始终为真 - 对称性(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
- 对于任何非 null 的引用值
x
,x.equals(null)
必须返回false
如果违反了这些规定,就会发现程序将会表现得不正常,甚至崩溃,而且很难找到程序的 Bug 所在。