Java - 基类和子类中的equals方法

时间:2012-10-31 16:08:58

标签: java equals

我有一个简单的基类,后来由许多单独的类扩展,这可能会引入新的字段,但不一定。我在基类中定义了一个equals方法,但是也为一些子类重写了它。可以在基础/子类中混合定义吗?在我的例子中,它是为了避免代码重复检查相同的字段。

8 个答案:

答案 0 :(得分:29)

看看Angelika Langer的"Implementing equals() To Allow Mixed-Type Comparison"

以下是一些问题的简要说明和可能的解决方案:

等于合同说(等等):

  

它是对称的:对于任何非空引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。

这意味着如果您的子类引入了新字段并且您正在将基类的对象(或不覆盖equals的另一个子类)与此子类的对象进行比较,则可能会出现问题。

请勿执行以下操作:

class BaseClass {
    private int field1 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof BaseClass) {
            return field1 == ((BaseClass) obj).field1;
        }
        return false;
    }
}

class BadSubClass extends BaseClass {
    private int field2 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof BadSubClass) {
            return super.equals(obj) 
                    && field2 == ((BadSubClass) obj).field2;
        }
        return false;
    }
}

因为你得到了

BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();

System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'

可能的解决方案:

替换instanceof - 检查班级比较:

obj != null && obj.getClass() == getClass()

使用此解决方案,BaseClass的对象永远不会等于任何子类的对象。

如果您创建另一个SubClass而没有@Override方法的equals,则两个SubClass - 对象可以彼此相等(如果BaseClass.equals检查确定开箱即用,但SubClass - 对象永远不会等于BaseClass - 对象。

良好的实施可以如下:

class BaseClass {
    private int field1 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj.getClass() == getClass()) {
            return field1 == ((BaseClass) obj).field1;
        }
        return false;
    }
}

class GoodSubClass extends BaseClass {
    private int field2 = 0;

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof GoodSubClass) {
            return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
        }
        return false;
    }
}

请参阅上面提到的文章,了解更多高级问题及其解决方案。

答案 1 :(得分:4)

不,在引入与equals方法相关的新字段时,不可能符合equals合同。有关更多信息,请参阅Joshua Bloch的“Effective Java”。

修改

我现在手头没有这本书,但我认为如果基类是抽象的/无法实例化的话就没问题。

答案 2 :(得分:0)

我认为只要您遵循eqauls()hashcode()合同就完全没问题了。

答案 3 :(得分:0)

您可以使用super()方法调用您正在扩展的类的方法,以防止需要代码重复

public class BaseClass {
  public boolean equals(BaseClass other) {
    return (other.getBlahblah() == this.Blahblah && .....);
  }
}

public class DerivedClass extends BaseClass {
  public boolean equals(DerivedClass other) {
    return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
  }
}

答案 4 :(得分:0)

相当有效的方法。问题出在您的一个子类中,必须保留由其父级绑定的equals定义。否则你有一个破坏的equals函数,这可能会在运行时产生一些非常独特的场景。

答案 5 :(得分:0)

我想,在equals(Object obj) hashCode() super class {{}}}中提供JavahashCode() and equals(Object obj)方法实现是完美的。我们都知道Java在基类java.lang.Object中提供override方法实现,并且在需要时我们class中的{{1}}。

答案 6 :(得分:0)

虽然以下内容并未处理所有情况,但我发现它非常实用。当我同时使用SuperClass和SubClass时,我已经多次使用过这个。我不想比较它们,但我也不想为SubClass重新实现所有SuperClass equals()。它处理:

  • a.equals(b)== b.equals(a)
  • 不重复字段比较代码
  • 轻松推广任何子类深度
  • Subclass.equals(SuperClass)== false
  • Superclass.equals(SubClass)== false

代码示例

// implement both strict and asymmetric equality
class SuperClass {
   public int f1;
   public boolean looseEquals(Object o) {
      if (!(o instanceof SuperClass)) return false;
      SuperClass other = (SuperClass)o;
      return f1 == other.f1;
   }
   @Override public boolean equals(Object o) {
      return looseEquals(o) && this.getClass() == o.getClass();
   }
}
class SubClass extends SuperClass {
   public int f2;
   @Override public boolean looseEquals(Object o) {
      if (!super.looseEquals(o)) return false;
      if (!(o instanceof SubClass)) return false;
      SubClass other = (SubClass)o;
      return f2 == other.f2;
   }
   // no need to override equals()
}

答案 7 :(得分:0)

如果您编写的代码不正确,则会造成严重的问题,称为 不对称(违反了平等的契约),所以让我们看看我们的选择。

最佳做法-同班教学策略。如果B是A的子类 每个类都有自己的equals方法,已实现 使用相同的类别策略,则类别B应该是 声明为final以防止引入不对称 B的任何未来子类中的equals定义。

问题。如果我们不希望B成为决赛怎么办?

使用合成代替继承。每当B和 A,其中B是A的子类,需要使用不同的equals方法, 使用合成代替继承是一个好策略,并且 如果不能将B类定为决赛,那是唯一安全的方法 处理平等。

如何?

 public class A{
public boolean equals(Object ob){
//write your code here
}
}

class B{
A a= new A();
public B(A a){
this.a= a;
}
public boolean equals(Object ob){
//...write your code here
if(!((B)ob).a).equals(a)) return false;
//...write your code here

}
}