如何正确改写equals方法

Creative Commons License
本作品采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可。

 

 

继承关系下的equals改写一文中,我简单介绍了Martin Odersky提出的canEqual方案,此方案可以正确改写基于继承关系下的equals方法。本文结合Effective Java一书中提到的相关描述并借鉴canEqual方法,重新整理出一套行之有效的改写equals的方案。敬请指正!

 

实现 canEqual的注意事项

首先需要指出的是,新改写方案主要依赖于canEqual方法,canEqual要完成的任务是:

判断当前对象(this)可以与哪些类型的目标对象进行内容上的比较

实现canEqual的方式比较自由,只要能完成上述任务即可。但在实现过程中需要注意:

1. canEqual的声明

canEqual方法的正确声明是:

public boolean canEqual(Object other);

其中参数 other就是目标对象。

2. canEqual方法不能抛出任何异常。

这一要点还可以解释为:使用instanceof而不是getClass方法来检查实参的类型。原因在于实参为null的场合下instanceof可以返回false,而 getClass方法将会抛出NullPointException。如使用instanceof的实现:

使用getClass方法的实现:

3. 确保实例化目标对象的类也存在canEqual方法

通常情况下,内容的比较来自于同一种类型的两个对象之间;特殊情况下可以看到来自于具有继承关系的两类对象之间的内容比较。针对于这两种情况,可以使用instanceof关键字实现 canEqual方法。这样做同样可以确保实例化目标对象所用的类一定存在canEqual方法。
但如果我们使用其它方式实现canEqual方法的话,请认真检查实例化目标对象的类是否存在canEqual方法。

 

改写equals的步骤

  1. 使用==操作符检查“实参是否为指向对象的一个引用”。如果是的话,则返回true。
  2. 使用当前对象(this)的canEqual方法检查是否可以与目标对象(实参)进行比较。如果不是的话,则返回false。
  3. 使用instanceof操作符(注1)检查“实参是否为正确的类型”。如果“正确”的类型不止一个,按优先级(注2)依次检查每一个“正确”的类型。如果所有的“正确”类型都不是,则返回false。
  4. 针对每一个符合要求的“正确”类型,做以下处理:
    1. 把实参转换到“正确”的类型。因为前面已经有了instanceof的检查,所以这个转换可确保成功。
    2. 使用目标对象(转换类型之后的实参)的canEqual方法检查是否可以与当前对象(this)进行比较。如果不是的话,则返回false(注3)
    3. 对于该类中每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配。检查完毕将结果直接返回(注3)
  5. 当你改写equals方法之后,问自己一个问题:equals处理的“正确”类型是否与canEqual方法中允许的“正确”类型等价(注4)

 

改写equals的注意事项

1. 使用instanceof而不是getClass方法来检查实参的类型。

原因参考实现canEqual的注意事项。

2. 正确处理多个“正确”类型的检查优先级。

当一个对象可以与多种类型的对象进行内容的比较时,类型检查的先后顺序可以根据业务的要求决定。通常情况下首先应该检查是否与自身类型相符合,其次检查其它类型。

3. 对于优先级高的正确类型,优先返回匹配结果。

在改写ColorPointEx的equals时(关于ColorPointEx的描述,请参考继承关系下的equals改写一文),正确类型有两个:继承自ColorPointEx的类与继承自Point的类,并且ColorPointEx的检查优先级高于Point。当一个目标对象在优先级高的类型(如ColorPointEx类型)比较下出现关键域内容不匹配的情况时,是否还需要进行下一个正确类型(如Point类型)的内容比较呢?回答是不需要。如下例:

此处我们应该不会再对两个对象进行Point类型的内容比较了。

4. equals中处理的“正确”类型必须与canEqual中允许的“正确”类型等价。

以ColorPointEx为例,如果改写的canEqual中允许的“正确”类型为继承自Point的所有类,但改写的equals中只处理了继承自ColorPoint的所有类这一种“正确”的类型,这种改写将会违反对称性原则。

 

附:Point与ColorPointEx的最终实现

作为单一类型比较的例子,正确改写Point的equals方法为:

作为多类型比较的例子,正确改写ColorPointEx的equals方法为:

相关文章
相关标签/搜索