【问题标题】:Force a class to override the .equals method强制一个类覆盖 .equals 方法
【发布时间】:2009-10-23 09:14:46
【问题描述】:

我有一堆实现通用接口的类:Command。

而这群类去了一个地图。

为了让 Map 正常工作,我需要每个实现 Command 的类重写 Object.equals(Object other) 方法。

没关系。

但我想强制覆盖等于。 => 当实现命令的东西不覆盖等于时,会出现编译错误。

这可能吗?

编辑:顺便说一句,我还需要强制覆盖哈希码...

【问题讨论】:

  • 接口命令 { public abstract boolean equals(Object that); }

标签: java interface overriding equals requirements


【解决方案1】:

不,你不能。但是,您可以做的是使用抽象基类而不是接口,并使equals() 抽象:

abstract class Command {
   // put other methods from Command interface here

   public abstract boolean equals(Object other);
   public abstract int hashCode();
}

Command 的子类必须然后提供它们自己的 equals 和 hashCode 方法。

强制 API 用户扩展基类通常是不好的做法,但在这种情况下可能是合理的。此外,如果您将Command 设为抽象基类而不是接口,而不是在addition 中引入人工基类到Command 接口,那么您的API 用户不会有错误的风险。

【讨论】:

  • 为了完整起见,您可能需要添加摘要 hashCode
  • 工作就像一个魅力,事实上,加上我已经有一个抽象基类......所以,这不是一个大的重构。谢谢
  • 在使用 Java 作为第一语言十多年后,我刚刚学到了一些新东西。
【解决方案2】:

您可以从抽象 XObject 而不是 java.lang.Object 扩展您的对象吗?

public abstract class XObject
 extends Object
{
@Override
public abstract boolean equals(Object o);
}

【讨论】:

  • 不过,不能保证 Command 的实现会扩展这个类。
  • 这是我最初要发布的内容:-)
  • 啊 ...但您可以声明您的其他 API 以要求您的抽象类(的子类)的实例,而不仅仅是 Command 实例。有点难看,但如果你真的想强制人们覆盖 equals,那就行了。
【解决方案3】:

如果你有一个孙子,抽象类将不起作用,因为它的父亲已经覆盖了 equals 和 hashCode 方法,然后你又遇到了问题。

尝试使用注释和 APT (http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html) 来完成它。

【讨论】:

    【解决方案4】:

    这只有在 Command 是一个接口或抽象类时才有可能,其中 equals(..) 是一个声明为抽象的方法。

    问题是Object,它是所有对象的超类,已经定义了这个方法。

    如果您希望指出这是一个问题(在运行时),您可以抛出异常,以强制您的 API 用户覆盖它。但至少据我所知,这在编译时是不可能的。

    尝试通过使用特定于 API 的方法来解决它,例如命令等于。另一种选择是(如前所述)扩展另一个定义 Equals 抽象方法的类。

    【讨论】:

    • 它仅适用于抽象类(如 Pierre 所述),不适用于接口。您不必实现接口中声明的 equals 方法,因为它已经存在于您的对象中。
    • 再读我的第二句话。
    【解决方案5】:

    你可以在interface Command中创建boolean myEquals(),然后像这样创建Adapter:

    class MyAdapter{
      Command c;
      boolean equals(Object x) {
        return c.myEquals((Command)x);
      }
    }
    

    那你就用map.put(key, new MyAdapter(command))代替map.put(key, command)

    【讨论】:

    • 这是最好的解决方案。另一个(使用抽象等于)不能确保抽象在未来仍然存在。如果在某个时候开发人员删除了这两个抽象,构建结果是好的,但 equals 不再起作用。使用此适配器可确保在有人更改代码时至少会出现编译器错误。
    【解决方案6】:

    如果您想要运行时检查,您可以执行以下操作:

        interface Foo{
    
    }
    class A implements Foo {
    
    }
    class B implements Foo {
        @Override
        public boolean equals(Object obj) {
            return super.equals(obj);
        }
    }
    
    public static void main(String[] args) {
        Class<A> clazzA = A.class;
        Class<B> clazzB = B.class;
    
        Class<Object> objectClass = Object.class;
        try {
            Method methodFromObject = objectClass.getMethod("equals",Object.class);
            Method methodFromA = clazzA.getMethod("equals",Object.class);
            Method methodFromB = clazzB.getMethod("equals",Object.class);
            System.out.println("Object == A" + methodFromObject.equals(methodFromA));
            System.out.println("Object == B" + methodFromObject.equals(methodFromB));
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    

    第一个打印 true,第二个打印 false。 如果你想要它编译时看起来唯一的选择是创建一个注释并使用注释处理工具来检查所有带注释的类是否覆盖等于。

    【讨论】:

      【解决方案7】:
      interface A{
          public boolean equal2(Object obj);
      }
      
      abstract class B implements A {
      
          @Override
          public boolean equals(Object obj) {
              return equal2(obj);
          }
      
      }
      
      
      class C extends B {
      
          public boolean equal2(Object obj) {
              throw new UnsupportedOperationException("Not supported yet.");
          }
      }
      

      【讨论】:

      • 因此 C 必须覆盖用于 equals 的方法。
      【解决方案8】:

      由于equals() 是从Object 继承的,我怀疑你不能真正强制这样做,因为对于每个 类型都有一个自动继承的equals() 实现可用。

      【讨论】:

        【解决方案9】:

        我认为不可能强制覆盖来自 Object 类的 equals。

        在相关说明中,请注意,当您覆盖 equals 时,您需要覆盖 Object 类中的“hashCode”方法。如果您打算将类的实例用作 Map 的键,这将变得尤为重要。检查这篇文章: http://www.artima.com/lejava/articles/equality.html 它提供了一些关于如何以正确的方式覆盖 equals 的提示

        【讨论】:

          【解决方案10】:

          正如其他答案已经解释的那样,不,你不能强迫你尝试做的事情。

          可能“足够”工作的一件事是定义第二个接口,称之为 MappableCommand。

          public interface MappableCommand 
          {
          
          }
          

          在此接口的文档中表明,如果类设计者考虑了您陈述的要求,则类应仅实现此(空)接口。

          然后你可以将你的地图的Value类型设置为MappableCommand,只有MappableCommands才能添加到地图中。

          这类似于为什么需要为可以通过 Java 的默认序列化机制序列化的类实现(空白)接口 Serializable 背后的逻辑。

          如果这不起作用,那么您可能不得不接受引发运行时错误;

          假编辑:

          如果你想让这个要求更明显,你可以这样定义新接口

          public interface MappableCommand 
          {
          
              public void iOverrodeTheEqualsMethod();
          
              public void seriouslyIPromiseThatIOverrodeIt();
          
          }
          

          【讨论】:

            【解决方案11】:

            以下是其他一些建议解决方案的变体:

            public abstract class CommandOverridingEquals implements Command {
                public abstract boolean equals(Object other);
                public abstract int hashcode();
            }
            
            Map<String, CommandOverridingEquals> map = 
                new HashMap<String, CommandOverridingEquals>();
            

            或者,如果您真的想确定,请使用已检查的哈希图;例如

            Map<String, CommandOverridingEquals> map = Collections.checkedMap(
                new HashMap<String, Command>(),
                String.class, CommandOverridingEquals.class);
            

            但无论你做什么,你都无法阻止别人这样做:

            public class AntiFascistCommand extends CommandOverridingEquals {
                public boolean equals(Object other) { return super.equals(other); }
                public int hashcode() { return super.hashcode(); }
                ...
            }
            

            我倾向于认为这种事情会在赛道上造成麻烦。例如,假设我有一堆现有的命令类,它们扩展了其他一些基类,并且(顺便)以规定的方式覆盖equalshashcode。问题是,我不能使用这些类。相反,我不得不重新实现它们或编写一堆包装器。

            IMO,试图强迫开发人员采用特定的实现模式是个坏主意。最好在 Javadocs 中加入一些强烈的警告并依靠开发人员做正确的事情

            【讨论】:

            • 当然,但是如果开发人员错过了 .equals 覆盖。多张地图会乱成一团。而且很难弄清楚。我承担风险。
            【解决方案12】:

            您是否可以为相关地图提供自己的java.util.comparator

            【讨论】:

              猜你喜欢
              • 2014-05-27
              • 2011-05-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-01-30
              • 1970-01-01
              • 2013-08-20
              • 1970-01-01
              相关资源
              最近更新 更多