equals()的实现:与实现的接口或实现类进行比较?

时间:2011-08-12 16:17:30

标签: java interface equals

我一直想知道如何最好地为一系列类实现equals(),这些类都实现了相同的接口(并且客户端应该只与所述接口一起工作,从不知道实现类)。

我还没有编写自己的具体示例,但JDK中有两个例子 - java.lang.Number和java.lang.CharSequence来说明决定:

boolean b1 = new Byte(0).equals( new Integer(0) ) );

或使用CharSequence

boolean b2 = "".equals(new StringBuilder());

您是否希望那些评估为真或不合适?这两种类型都实现了相同的数据类型接口,并且作为使用Numbers(resp.CharSequences)实例的客户端,如果equals将比较接口类型而不是实现类型,我将有更轻松的生活。

现在这不是一个理想的例子,因为JDK向公众公开了实现类型,但是假设我们没有必须维护已经存在的内容 - 从设计者的角度来看:应该等于对接口的检查,或者它是否更好,检查实现?


注意:我知道检查接口的相等性可能非常难在实践中实际正确实现,甚至更棘手,因为相同的接口也需要返回相同的hashCode()。 但这些只是实现中的障碍,例如CharSequence,虽然接口非常小,但是相等检查所需的一切都没有透露实现的内部结构(所以它主要可以正确实现,甚至没有提前了解未来的实施)。 但我对设计方面更感兴趣,而不是如何实际实现它。我不会仅根据实施的难度来决定。

6 个答案:

答案 0 :(得分:5)

定义一个抽象类implements你的接口并定义最终的equals()/ hashCode()方法,并让你的客户扩展它:

public interface Somethingable {
    public void something();
}

public abstract class AbstractSomethingable implements Somethingable {
    public final boolean equals(Object obj) {
        // your consistent implementation
    }

    public final int hashCode() {
        // your consistent implementation
    }
}

请注意,通过创建类abstract,您可以implements界面而无需定义界面的方法。

您的客户仍然需要实施something()方法,但他们的所有实例都会使用您的代码来实现equals()/ hashCode()(因为您已经制作了这些方法{{ 1}})。

与客户的不同之处在于:

  • 使用final关键字代替extends关键字(次要)
  • 无法扩展他们选择使用您的API的其他类别(可能是次要的,可能是主要的 - 如果它可以接受,那就去吧)。

答案 1 :(得分:3)

我通常会认为“相似”的对象不会相等 - 例如我不希望Integer(1)会传递equals(Long(1))。我可以想象那些合情合理的情况,但由于jdk需要是一个通用的API,你不能假设总是是正确的事情。

如果你有一些合理的自定义对象,我认为如果你

那么实现一个扩展的equals定义是完全没问题的。
  • 确定你没有一些边缘情况,你确实需要更具体的相等(即需要相同的类)
  • 非常清楚地记录
  • 确保hashcode的行为与您的新等号一致。

答案 2 :(得分:0)

对于它的价值,我可能会做一个特定于实现的equals实现(旁注 - 不要忘记实现hashCode ......)。接口级equals()给接口的实现者带来了沉重的负担 - 他们可能会也可能不会意识到特殊要求。

通常,实现级别工作正常,因为您的客户端只处理一个实现(即MyNumberProcessor 可以对任何数字起作用,但实际上它的一个实例只需要处理Long,也许只能处理另一个双)。泛型是确保发生这种情况的好方法。

在极少数情况下它很重要,我可能会设计客户端以允许注入比较器,或者 - 如果不可用 - 将我的Numbers封装到VarTypeNumber中。

答案 3 :(得分:0)

我会考虑对两个没有相同具体类型的对象返回true的equals实现非常“令人惊讶”的行为。如果你在编译时知道接口的每个可能的实现者的盒子内操作,你可以用仅有接口方法来构造有意义的等号,但这对于API /框架代码来说并不现实。

当你调用用来实现equals的方法时,你甚至无法确定没有人会编写一个改变其内部状态的接口实现!谈论混淆,等于检查返回true并在此过程中使自身无效?

-

就我所知,这是“检查与界面的平等”的问题:

public interface Car {

  int speedKMH();
  String directionCardinal();

}

public class BoringCorrolla implements Car {

  private int speed;
  private String directionCardinal;

  public int speedKMH() { return speed; }
  public String directionCardinal() { return directionCardinal; }

  @Override
  public boolean equals(Object obj) {
    if (obj isntanceof Car) {
      Car other = (Car) obj;
      return (other.speedKMH() == speedKMH() && other.directionCardinal().equals(directionCardinal());
    }
  }
}

public class CRAZYTAXI implements Car, RandomCar {

      public int speedKMH() { return randomSpeed(); }
      public String directionCardinal() { return randomDirection();}
    }

答案 4 :(得分:0)

我尝试在界面中添加另一个equals方法。怎么样:

assertFalse(new Integer(0).equals(new Byte(0))); // pass
assertTrue(new Integer(0).valueEquals(new Byte(0))); // hypothetical pass

这不会产生意外行为(不同的类型相等),但保持开放的可能性以检查相等的值。

在有效的java中有一个相关的主题,其中讨论了与instanceof和getClass的等价。但不记得物品编号。

答案 5 :(得分:0)

可以在不同的类之间定义相等。

在您的情况下,必须由接口指定完全相等的算法,因此实现接口的任何类都必须遵守它。更好的是,由于算法仅依赖于由界面暴露的信息,因此只需实现它,因此子类可以简单地借用它。

interface Foo

class Util 

    static int hashCode(Foo foo){ ... }

    static boolean equal(Foo a, Foo b){ ... }

    static boolean equal(Foo a, Object b)
        return (b instanceof Foo) && equal(a, (Foo)b);

class FooX implements Foo

    int hashCode()
        return Util.hashCode(this); 

    boolean equals(Object that)
        return Util.equal(this, that);