【不覆盖equals的情形】
不覆盖equals的情况下,类的每个实例都与它自身相等,如果满足以下任何一个条件,就是所期望的结果:
- 类的每个实例本质上都是唯一的
- 不关心类是否提供了"逻辑相等"的测试功能
- 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的(要小心)
类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。在这种情况下,无疑是应该覆盖equals方法的,以防它被以外调用【这种情况下,只是对equals的一种废弃,并没有新加什么功能】
@override
public boolean equals(Object obj){
throw new AssertionError();
}
【覆盖equals的情形】
如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且父类还没有覆盖equals以实现期望行为,这时就需要覆盖equals方法。(比如要判断两个student
是否是同一个人,这个时候我们就需要按需重写equals)
【覆盖equals的通用约定】
equals方法实现了等价关系(equivalence relation):
- 自反性(reflexive) 对于任何非null的引用值x,x.equals(x)必须返回true
- 对称性(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
【实现高质量equals的诀窍】
- 使用==操作符检查“参数是否为这个对象的引用”。如果是则返回true。这是一种性能优化
if(this==obj)
return true;
- 使用instanceof操作符检查“参数是否为正确的类型”,如果不是则返回false。所谓的正确的类型是指equals方法所在的那个类,或者是该类的父类或接口
- 把参数转化成正确的类型:因为上一步已经做过instanceof测试,所以确保转化会成功
- 对于该类的每个“关键”域,检查参数中的域是否与该对象中对应的域相匹配(其实就是比较两个对象的值是否相等了)
- 当你编写完equals方法之后,应该问自己三个问题:它是否是对称的、传递的、一致的?当然equals也必须满足其他两个约定(自反性、非空性)但是这两种约定通常会自动满足
【additional】
- 覆盖equals时总要覆盖hashCode
- 不要企图让equals方法过于智能:不要想过度地去寻求各种等价关系,否则容易陷入各种麻烦
- 不要将equals声明中的Object对象替换为其他类型,不然就不是重写equals了,而是重载了。加上@override可以避免这种错误发生。