我有一堆实现公共接口的类:Command。
这一类课程都转到了Map。
为了使Map正常工作,我需要每个实现Command的类都覆盖Object.equals(Object other)
方法。
没关系。
但我想强迫平等的压倒一切。 =>当实现命令的东西不重写等于时,会出现编译错误。
这可能吗?
编辑:BTW,我还需要强制重写哈希码......
答案 0 :(得分:81)
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
成为抽象基类而不是接口,而不是在添加中将一个人工基类引入Command接口,那么您的API用户就不会有它的风险错。
答案 1 :(得分:17)
您可以从抽象XObject扩展对象而不是java.lang.Object吗?
public abstract class XObject
extends Object
{
@Override
public abstract boolean equals(Object o);
}
答案 2 :(得分:4)
如果你有一个孙子,抽象类将不起作用,因为它的父亲已经覆盖了equals和hashCode方法,然后你又重新遇到了你的问题。
尝试使用annotatins和APT(http://docs.oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html)来完成它。
答案 3 :(得分:1)
只有当Command是一个接口或一个抽象类时,这才有可能,其中equals(..)是一个声明为abstract的方法。
问题是Object是所有对象的超类,已经定义了这个方法。
如果您希望表明这是一个问题(在运行时),您可以抛出异常,强制您的API用户覆盖它。但是在编译时这是不可能的,至少据我所知。
尝试使用特定于API的方法解决此问题,例如: CommandEquals。另一个选项是(如上所述)扩展另一个定义Equals抽象方法的类。
答案 4 :(得分:1)
您可以在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)
答案 5 :(得分:1)
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.");
}
}
答案 6 :(得分:1)
如果您想要运行时检查,可以执行以下操作:
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 :(得分:0)
由于equals()
是从Object
继承而来的,我怀疑你不能强迫它,因为对于每个类型,都有equals()
的自动继承实现可用。
答案 8 :(得分:0)
我认为不可能强制覆盖equals,因为它来自Object类。
在相关的说明中,请注意,在覆盖equals时,您需要覆盖Object类中的'hashCode'方法。如果要将类的实例用作Map的键,这将变得非常重要。查看这篇文章: http://www.artima.com/lejava/articles/equality.html 它提供了一些关于如何以正确的方式覆盖equals的提示
答案 9 :(得分:0)
正如其他答案已经解释过的那样,你不能强迫你尝试的那种东西。
可能“足够”的一件事是定义第二个接口,将其称为MappableCommand。
public interface MappableCommand
{
}
在此接口的文档中指出,如果类设计者已考虑您所述的要求,则类应仅实现此(空)接口。
然后,您可以将地图的值类型设置为MappableCommand,并且只能将MappableCommands添加到地图中。
这类似于为什么需要为可以通过Java的默认序列化机制序列化的类实现(空白)接口Serializable的逻辑。
如果这不起作用,那么您可能不得不解决投掷运行时错误;
虚假编辑:
如果你想让这个要求更明显,你可以用这种方式定义新界面
public interface MappableCommand
{
public void iOverrodeTheEqualsMethod();
public void seriouslyIPromiseThatIOverrodeIt();
}
答案 10 :(得分:0)
以下是其他一些建议解决方案的变体:
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; e.g。
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中添加一些强烈警告,并依赖开发人员做正确的事情。
答案 11 :(得分:0)
您是否可以将自己的java.util.comparator
提供给相关地图?