2

我一直在阅读有关在处理子类时如何最好地覆盖 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。我不允许更改设计,因此不能选择使用合成。有什么建议么?

4

3 回答 3

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;
    }
}
于 2013-05-21T13:15:52.153 回答
1

如果你有equals两个,ConcreteClassOne然后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,但在相反的情况下,类似的检查通过了。

于 2013-05-21T13:27:02.080 回答
0

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

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

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

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

于 2013-05-21T18:03:58.567 回答