代码之家  ›  专栏  ›  技术社区  ›  Antoine Claval

强制类重写.equals方法

  •  50
  • Antoine Claval  · 技术社区  · 15 年前

    我有许多类实现了一个公共接口:command。

    这群学生去看地图。

    为了使映射正常工作,我需要实现命令的每个类重写 Object.equals(Object other) 方法。

    很好。

    但是我想强迫平等的人压倒一切。=>当实现命令的某个对象不重写等于时出现编译错误。

    可能吗?

    编辑:顺便说一句,我还需要强制重写hashcode…

    12 回复  |  直到 10 年前
        1
  •  79
  •   skaffman    15 年前

    不,您不能。但是,您可以使用抽象基类而不是接口,并使 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用户扩展基类通常是不好的做法,但在这种情况下可能是合理的。另外,如果你 命令 抽象基类而不是接口,而不是在 附加 对于命令接口,那么您的API用户就不会出错。

        2
  •  17
  •   Ciro Santilli OurBigBook.com    10 年前

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

    public abstract class XObject
     extends Object
    {
    @Override
    public abstract boolean equals(Object o);
    }
    
        3
  •  4
  •   Philippe Gioseffi    12 年前

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

    尝试使用annotatins和apt( http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html )完成任务。

        4
  •  1
  •   NT_    15 年前

    只有当命令是接口或抽象类,其中equals(..)是声明为抽象的方法时,这才可能发生。

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

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

    尝试通过一个特定于API的方法来解决这个问题,例如commandEquals。另一个选项是(如前所述)扩展另一个类,该类定义一个等号抽象方法。

        5
  •  1
  •   Nikolay Ivanov    15 年前

    如果你想要一个运行时检查,你可以做如下的事情:

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

    将第一个打印为真,第二个打印为假。 如果您希望编译时看起来像唯一的选项是创建一个注释,并使用注释处理工具检查所有带注释的类重写是否相等。

        6
  •  1
  •   YetAnotherUser    12 年前
    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.");
        }
    }
    
        7
  •  0
  •   Joey Gumbo    15 年前

    自从 equals() 继承自 Object 我怀疑你不能真的强迫你,因为 每一个 类型有一个自动继承的实现 等式() 可用。

        8
  •  0
  •   sateesh    15 年前

    我认为不可能强制重写来自对象类的equals。

    请注意,当重写等于时,需要重写对象类中的“hashcode”方法。如果要将类的实例用作映射的键,这就变得尤为重要。查看本文: http://www.artima.com/lejava/articles/equality.html 它提供了一些关于如何以正确的方式重写equals的提示

        9
  •  0
  •   Tom Neyland    15 年前

    正如其他答案已经解释过的那样,不,你不能强迫自己去做那种事情。

    “足够”工作的一件事是定义第二个接口,调用这一个mappablecommand。

    public interface MappableCommand 
    {
    
    }
    

    在这个接口的文档中,如果类设计器已经考虑了您所声明的需求,那么类应该只实现这个(空)接口。

    然后,可以将映射的值类型设置为mappablecommand,并且只有mappablecommands才能添加到映射中。

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

    如果这不起作用,那么您可能不得不解决抛出运行时错误的问题;

    伪造编辑:

    如果您想让这个需求更加明显,您可以这样定义新的接口

    public interface MappableCommand 
    {
    
        public void iOverrodeTheEqualsMethod();
    
        public void seriouslyIPromiseThatIOverrodeIt();
    
    }
    
        10
  •  0
  •   Niki Tonsky    15 年前

    你可以创造 boolean myEquals() 在里面 interface Command ,并创建如下适配器:

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

    那你就用 map.put(key, new MyAdapter(command)) 而不是 map.put(key, command)

        11
  •  0
  •   Stephen C    15 年前

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

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

    或者,如果您真的想确定,可以使用选中的hashmap;例如。

    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(); }
        ...
    }
    

    我倾向于认为这类事情会给你带来麻烦。例如,假设我有一堆现有的命令类,它们扩展了一些其他的基类,并且(顺便说一下)重写 equals hashcode 以规定的方式。问题是,我不能使用这些类。相反,我不得不重新实现它们或者写一堆包装纸。

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

        12
  •  0
  •   DerMike Vladimír Schäfer    15 年前

    你能提供你自己的吗 java.util.comparator 到有问题的地图?