【问题标题】:Enforce "equals" in an interface在界面中强制“等于”
【发布时间】:2010-06-10 06:19:25
【问题描述】:

我有一个接口,我希望实现这个接口的每个人都实现一个覆盖的“equals”方法。

有没有办法确保发生这种情况?

我猜这会发生的方式是实现我的接口的类将自动从 Object 获取等值,因此使接口快乐。

【问题讨论】:

  • 你这样做的理由是什么?
  • 这个问题出现时我遇到的具体案例;我有几个类实现的接口。这些类由唯一标识符标识。因此,如果两个标识符相同,则应将对象视为相等。有一次,我有一个这些类之一的列表,并且想知道列表中是否已经存在一个新对象。我认为使用 List.contains(..) 进行此检查会很优雅。但这需要我可以确定 equals 已被覆盖。不过用其他方法很容易解决。抽象类也可以很好地工作。

标签: java


【解决方案1】:

不行,你只能创建一个抽象类而不是这样的接口:

public abstract class MyApi {

  public final boolean equals(Object other) {
    if (other == this) {
      return true;
    }
    if (other instanceof MyApi) {
      return equals((MyApi)other);
    }
    return false;
  }

  protected abstract boolean equals(MyApi other);

}

或更简单的版本:

public abstract class MyApi {

  public boolean equals(Object other) {
    throw new UnsupportedOperationException("equals() not overridden: " + getClass());
  }

}

编辑(在@CodeConfident 发表评论后试一试,谢谢!从没想过它会起作用):

您也可以简单地在抽象类中声明 equals()(而不是在接口中!),从而隐藏 Object 实现并在任何子类中强制执行新实现:

public abstract class MyApi {

  public abstract boolean equals(Object obj);

  public abstract int hashCode();

}

无论如何,您应该始终一起执行equals()hashCode() 来履行合同。

【讨论】:

  • 如果你提倡使用抽象类,为什么不将equals(Object)声明为抽象类呢?
  • @ilmtitan:您不能声明继承的方法抽象!
  • @ArneBurmeister - 这不是真的(至少现在是这样)。您绝对可以使用抽象类中的抽象方法覆盖继承的方法。看到这个答案:stackoverflow.com/questions/1718112/…
  • 你能不能把你的第一个方法简化为return (other == this) || (other instanceof MyApi && equals((MyApi) other));(顺便说一下+1;我喜欢你的方法)
  • @CodeConfident 你是对的,我从来没有这样做过,甚至没想到它会起作用,谢谢!
【解决方案2】:

没有。您可以将其添加到接口(以及 javadocs),但如果 Object.equals 具有相同的签名,则不能让编译器让它们覆盖它。

【讨论】:

    【解决方案3】:

    没有。接口是保证方法存在的契约。

    没有强制要求在接口中重写方法的机制。

    【讨论】:

      【解决方案4】:

      编辑:可能不是一个好主意(请参阅 FarmBoy 的评论)。留给子孙后代。

      不要使用Object 类中的equals(Object obj),而是让他们将其与您的接口实现进行比较。

      public interface MyInterface {
        public boolean equals(MyInterface mi);
      }
      

      因此,

      public class MyImplementation implements MyInterface {
        public boolean equals(MyInterface mi)
        {
          if(this == mi)
            return true;
          // for example, let's say that each implementation
          // is like a snowflake...(or something)
          return false;
        }
      }
      

      然后:

      public class Main {
      
        public static void main(String[] args)
        {
          Object o = new MyImplementation();
          MyImplementation mi1 = new MyImplementation();
          MyImplementation mi2 = new MyImplementation();
      
          // uses Object.equals(Object)
          o.equals(mi1);
          // uses MyImplementation.equals(MyInterface)
          mi1.equals(mi2);
          // uses Object.equals(Object)
          mi2.equals(o);
        }
      }
      

      【讨论】:

      • 这不是一个好主意。任何调用equals的东西(比如将一个对象放入Set不会调用这个equals,而是'真正的'一个。所以你会强制实现类实现这个方法,任何优秀的程序员都会实现'真正的'一个也是。
      • 好点 - 我没有想到这一点。我希望提问者更详细地了解这个问题背后的动机。
      【解决方案5】:

      我想可能有两个原因导致需要这种 equals 方法

      1. 您希望确保应用程序中的所有类(或它们的子集)“将”具有 equals 方法。诸如标准执行之类的事情,或者确保您使用的某些 API 按应有的方式工作(并且他们希望 equals 能够正确实现。假设您经常使用 Maps 并希望绝对确定类的子集是绝对可能的 map键) 如果是这样的话,这不是要走的路。在这种情况下,您将无法做到这一点,但即使您做到了,也不会是正确的。您应该使用代码覆盖工具和更好的单元测试。

      2. 您不想要等于方法,但想要一个类似的方法。在这种情况下,您可以在界面中创建另一个具有相似名称的方法。

      【讨论】:

        【解决方案6】:

        我已经尝试在 JavaDoc 中为equals 编写所需的合同。但是hashCodeequals 一致的要求导致hashCode 的合约非常复杂。所以我放弃了,而是用final这两种方法的实现创建了抽象基类。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-11
          • 2019-06-15
          • 2011-10-10
          相关资源
          最近更新 更多