overriding equals method when dealing with inheritance

拈花ヽ惹草 提交于 2019-12-20 03:30:21

问题


I have been reading about how best to override the equals method when dealing with subclasses and here I have found quite a few posts. They recommend different ways of implementing a solution using instanceof or getClass() to compare objects of different sub classes.

However with reference to Effective Java, my understanding is (and I am new to this so I may well be wrong!) Bloch argues that in the end both can be problematic, “There is no way to extend an instantiable class and add a value component while preserving the equals contract, unless you are willing to forgo the benefits of object-oriented abstraction”. Then recommends to “favour composition over inheritance”.

So I am dealing with this class hierarchy: AbstractClass, ConcreteClass1 and ConcreteClass2. ConcreteClass1 extends AbstractClass and ConcreteClass2 extends ConcreteClass1. At the moment only AbstractClass overrides the equals method.

So in 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);
        }

    }

And in ConcreteClass1 I have:

public class ConcreteClassOne extends AbstractClass
{
  private final AbstractClass parent;

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

    parent = aParent;
  }

}

Finally in ConcreteClassTwo I have:

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

  private final int trackingNo;

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

    trackingNo= getNextTrackingNo();
  }
}

So I believe I need to override equals method in both ConcreteClassOne and ConcreteClassTwo to include the significant fields parent and trackingNo. I'm not allowed to change the design so using composition is not an option. Any suggestions?


回答1:


The simplest approach is to extend equals() in both the concrete and the abstract classes.

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;
    }
}



回答2:


If you have equals both in ConcreteClassOne and ConcreteClassTwo then the symmetry of equals is broken:

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

Now, if you implement equals the usual way, this is going to print

true
false

because in c2.equals you have instanceof ConcreteClassTwo, which fails for c1, but in the opposite case the analogous check passes.




回答3:


The base class contract should specify one of two approaches: either it should declare that no derived-class object should consider itself to any other object that is not of the exact same class, or else it should specify that every derived-class object should be convertible to a canonical form defined by the base-class contract, and two immutable objects should be considered equivalent if their canonical forms would match.

An example of the latter situation would be an ImmutableSquareFloatMatrix base class with methods int GetSize() and float GetCell(int row, int column). A common implementation would have an array of (size*size) float values in it, but one could also have e.g. ZeroMatrix and IdentityMatrix classes whose only field specified the size, a ConstantMatrix class with a field specifying the size and a field specifying a value which should be returned for every cell, a DiagonalMatrix class with a single-dimensional array just containing items for the diagonal (the GetCell method would return zero for everything else), etc.

Given two instances of classes derived from ImmutableSquareFloatMatrix, it would be possible to compare them by comparing their sizes and then comparing all the values therein, but that would in many cases be inefficient. If either of the objects being compared "knows" about the other object's type, it may be possible to improve efficiency enormously. If neither object knows about the other, falling back to a default comparison method may be slow, but would yield correct results in any case.

A workable approach to handle that situation may be to have the base type implement an equals2 method, which return 1 if its special knowledge of the other object meant it could tell it was equal, -1 if it could tell it was not equal, or 0 if it couldn't tell. If either type's equals2 method knows they're unequal, they're unequal. Otherwise, if either knows they're equal, they're equal. Otherwise, test equality using cell-by-cell comparison.



来源:https://stackoverflow.com/questions/16670824/overriding-equals-method-when-dealing-with-inheritance

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!