24

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

4

8 回答 8

49

查看 Angelika Langer 的“实现 equals() 以允许混合类型比较”

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

平等合同说(除其他外):

它是对称的:对于任何非空引用值 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-check 替换为类比较:

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

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

如果您在没有方法的情况下创建另一个,则SubClass两个-objects可以开箱即用地彼此相等(如果检查决定如此),但-object 永远不会等于-object。@OverrideequalsSubClassBaseClass.equalsSubClassBaseClass

一个好的实现可能如下:

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

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

于 2012-10-31T17:52:58.960 回答
4

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

编辑:

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

于 2012-10-31T16:17:26.433 回答
1

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

  • a.equals(b) == b.equals(a)
  • 不重复字段比较代码
  • 轻松推广到任何子类深度
  • Subclass.equals(SuperClass) == 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()
}
于 2018-01-19T14:57:10.727 回答
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.....);
  }
}
于 2012-10-31T16:13:24.023 回答
0

相当有效的方法。问题出在您的一个子类中,它必须保留 equals 的定义,因为它受其父类的约束。否则你有一个损坏的 equals 函数,这可能会在运行时导致一些非常独特的场景。

于 2012-10-31T16:16:21.770 回答
0

我想,像以前那样提供andequals(Object obj)方法hashCode()实现是完美的。我们都知道 Java在基类java.lang.Object中提供了方法实现,并且在需要时我们将它们放在我们的.super classJavahashCode() and equals(Object obj)overrideclass

于 2012-10-31T16:24:20.763 回答
0

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

最佳实践——同班策略。如果 B 是 A 的子类,并且每个类都有自己的 equals 方法,使用相同的类策略实现,则应将 B 类声明为 final,以防止在 B 的任何未来子类中引入不对称的 equals 定义。

问题。如果我们不想让 B 成为最终版本怎么办?

使用组合而不是继承。每当 B 类和 A 类(其中 B 是 A 的子类)需要不同的 equals 方法时,使用组合而不是继承是一个好策略,如果不能选择使 B 类成为 final,那么它是处理 equals 的唯一安全方法.

如何?

 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

}
}
于 2019-09-25T04:49:16.837 回答
-1

我认为只要您遵循eqauls()hashcode()合同就可以了。

于 2012-10-31T16:11:37.680 回答