在处理继承时重写equals方法

时间:2013-05-21 13:07:27

标签: java inheritance override equals abstraction

我一直在阅读在处理子类时如何最好地覆盖equals方法,在这里我发现了不少帖子。他们建议使用instanceof或getClass()来实现解决方案的不同方法来比较不同子类的对象。

然而,参考Effective Java,我的理解是(我是新手,所以我可能错了!)Bloch认为最终两者都有问题,“没有办法扩展可实例化的类,在保留equals合同的同时添加一个值组件,除非你愿意放弃面向对象抽象的好处“。然后建议“赞成合成而非继承”。

所以我正在处理这个类层次结构:AbstractClass,ConcreteClass1和ConcreteClass2。 ConcreteClass1扩展了AbstractClass,ConcreteClass2扩展了ConcreteClass1。目前只有AbstractClass重写了equals方法。

所以在AbstractClass中:

public abstract class AbstractClass {
        private String id;


        public boolean equals(Object other) {
            return other != null && other.getClass().equals(getClass())
                    && id.equals(((AbstractClass) other).id);
        }

    }

在ConcreteClass1中,我有:

public class ConcreteClassOne extends AbstractClass
{
  private final AbstractClass parent;

  public ConcreteClassOne( String anId, AbstractClass aParent )
  {
    super( anId );

    parent = aParent;
  }

}

最后在ConcreteClassTwo中我有:

public class ConcreteClassTwo extends ConcreteClassOne
{
  private static int nextTrackingNo = 0;

  private final int trackingNo;

  public ConcreteClassTwo ( String anId )
  {
    super( anId, null );

    trackingNo= getNextTrackingNo();
  }
}

所以我认为我需要在ConcreteClassOne和ConcreteClassTwo中重写equals方法以包含有效字段parent和trackingNo。我不允许更改设计,因此使用合成不是一种选择。有什么建议?

3 个答案:

答案 0 :(得分:2)

最简单的方法是在具体和抽象类中扩展equals()。

public class ConcreteClassTwo extends ConcreteClassOne {
    public boolean equals(Object other) {
        boolean rv = super.equals( other );
        if ( other instanceof ConcreteClassTwo ) {
           rv = rv && (this.trackingNo == ((ConcreteClassTwo) other).trackingNo);
        }
        return rv;
    }
}

答案 1 :(得分:1)

如果您equalsConcreteClassOne都有ConcreteClassTwo,那么equals的对称性就会被破坏:

Object c1 = new ConcreteClassOne(),
       c2 = new ConcreteClassTwo();
System.out.println("c1=c2? " + c1.equals(c2)");
System.out.println("c2=c1? " + c2.equals(c1)");

现在,如果以通常的方式实现equals,则打印

true
false

因为在c2.equals instanceof ConcreteClassTwo c1,{{1}}失败,但在相反的情况下,类似的检查通过。

答案 2 :(得分:0)

基类合约应该指定两种方法之一:要么声明没有派生类对象应该将自己视为不属于完全相同的类的任何其他对象,要么它应该指定每个派生类对象应该可以转换为由基类契约定义的规范形式,并且如果它们的规范形式匹配,则应该认为两个不可变对象是等价的。

后一种情况的一个例子是使用方法int GetSize()float GetCell(int row, int column)的ImmutableSquareFloatMatrix基类。一个常见的实现将在其中具有(size * size)浮点值的数组,但是也可以具有例如ZeroMatrixIdentityMatrix类,其唯一字段指定大小,ConstantMatrix类,其中一个字段指定大小,一个字段指定应为每个单元格返回的值,{{1包含一维数组的类只包含对角线的项(DiagonalMatrix方法将为其他所有方法返回零)等等。

鉴于从GetCell派生的两个类的实例,可以通过比较它们的大小然后比较其中的所有值来比较它们,但在许多情况下这将是低效的。如果被比较的任何一个对象“知道”另一个对象的类型,则可能极大地提高效率。如果两个对象都不知道另一个对象,那么回退到默认比较方法可能会很慢,但无论如何都会产生正确的结果。

处理这种情况的可行方法可能是让基类型实现一个ImmutableSquareFloatMatrix方法,如果它对另一个对象的特殊知识意味着它可以告诉它是相等的,则返回1,如果它可以,则返回-1告诉它不相等,如果它不能说,则为0。如果任何一种类型的equals2方法都知道它们是不相等的,那么它们就是不相等的。否则,如果他们知道他们是平等的,他们是平等的。否则,使用逐个单元格比较测试相等性。