toString(),equals()和hashCode()

所以,我有一个需要实现的一堆方法的接口,方法名称是不相关的。

实现这个接口的对象通常被放入集合中,并且还有我希望它们使用的特殊的toString()格式。

所以,我认为将hashCode(),equals()和toString()放入界面会很方便,以确保我记得覆盖这些的默认方法。 但是,当我将这些方法添加到接口时,如果我没有实现这三个方法,那么IDE /编译器就不会抱怨,即使我明确地将它们放在接口中。

为什么这不会被强制执行? 它抱怨说,如果我不执行任何其他方法,但它不执行这三个。 是什么赋予了? 任何线索?

Java中的所有对象都inheritance自java.lang.Object , Object提供了这些方法的默认实现。

如果你的接口包含其他方法,那么如果你没有通过提供这些方法的实现来完全实现这个接口的话,Java会发出抱怨。 但是在equals()hashCode()toString() (以及其他一些你没有提到的)的情况下,实现已经存在。

你可能能够完成你想要的一种方法是通过在接口中提供一个不同的方法,比如说,向toPrettyString()或类似的东西。 然后你可以调用这个方法而不是默认的toString()方法。

这听起来像你想强制你的类覆盖这些方法的默认实现。 如果是这样,那么做到这一点的方法是声明一个抽象的超类,其方法被声明为抽象的。 例如:

 public abstract class MyBaseClass implements ... /* existing interface(s) */ { public abstract boolean equals(Object other); public abstract int hashCode(); public abstract String toString(); } 

然后改变你当前的类来extend这个类。

这种方法有效,但不是一个理想的解决scheme。

  • 对于现有的类层次结构可能会有问题。

  • 强制实现现有接口的类扩展特定的抽象类是一个坏主意。 例如,您可以更改方法签名中的参数,以使用抽象类而不是现有接口。 但最终的结果是不太灵活的代码。 (无论如何,人们可以find方法去颠覆这个方法;例如,通过添加自己的抽象子类来“实现” super.<method>(...)调用)!

  • 实施特定的类层次结构/实现模式是短视的。 您无法预测未来的需求变化是否意味着您的限制会造成困难。 (这就是为什么人们build议针对接口而不是特定类进行编程的原因。


回到你的实际问题,为什么你的界面不强制类重新声明这些方法:

为什么这不会被强制执行? 它抱怨说,如果我不执行任何其他方法,但它不执行这三个。 是什么赋予了? 任何线索?

一个接口强加了一个约束,即实现它的具体类具有每个方法的实现。 但是,它并不要求类本身实现这些方法。 方法实现可以从超类inheritance。 在这种情况下,这就是发生的事情。 从java.lang.Objectinheritance的方法非常适合约束。

JLS 8.1.5声明如下:

“除非被声明的类是抽象的,否则每个直接超级接口的所有抽象成员方法必须通过本类中的声明或从直接超类或直接超级接口inheritance的现有方法声明来实现(第8.4.8.1节)因为不抽象的类不允许有抽象方法(§8.1.1.1)。“

所有这三种方法都由java.lang.Object定义,它被所有其他类(隐式)扩展; 因此这些方法的默认实现存在,编译器没有什么可抱怨的。

任何实现你的接口的类也会扩展Object。 对象定义了hashCode,equals和toString,并且有三个默认实现。

你试图达到的是好的,但不可行。

这些方法有一个来自Object

你的对象已经包含了这三个方法的实现,因为每个对象都从Objectinheritance这些方法,除非它们被覆盖。

如果你想强制重写equals()和hashCode(),那么从一个抽象超类扩展,这个抽象超类将这些方法定义为抽象的。

Java只关心方法是在什么地方定义的。 如果已经定义了接口,那么接口不会强制您重新定义从接口inheritance的新类中的方法。 由于java.lang.Object已经实现了这些方法,所以即使它们没有自己重写这三个方法,你的新对象也符合这个接口。

其他人已经充分回答了你的实际问题。 至于你的特定问题的解决scheme,你可能会考虑创build你自己的方法(可能是getStringRepresentation,getCustomHashcode和equalsObject),并让你的对象扩展一个基类,它的equals,toString和hashCode方法调用这些方法。

不过,这可能会首先破坏使用界面的目的。 这是一些人build议equals,toString和hashCode不应该包含在Object类中的原因之一。

亚当给你的理由为什么你不能逃脱尝试强制equals,hashCode和toString。 我会去接触Adam和Stephan提供的解决scheme的以下实现:

 public interface Distinct { boolean checkEquals(Object other); int hash(); } public interface Stringable { String asString(); } public abstract class DistinctStringableObject { @Override public final boolean equals(Object other) { return checkEquals(); } @Override public final int hashCode() { return hash(); } @Override public final String toString() { return asString(); } } 

现在,任何需要自己明确区分和表示为String的类都可以扩展DistinctStringableObject,强制执行checkEquals,hashCode和toString。

示例具体类:

 public abstract class MyDistinctStringableObject extends DistinctStringableObject { @Override public final boolean checkEquals(Object other) { ... } @Override public final int hash() { ... } @Override public final String asString() { ... } } 

抽象类不会工作,如果你有一个孙子,因为它的父亲已经覆盖了equals和hashCode方法,然后你再次从头开始。

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

以及如果你已经声明了一个接口,默认情况下所有的方法都是抽象的,你需要提供这个function,但是当你在子类中实现它时,你提供了实现的权利。 你可以看到每个类都是一个超类的子类(简单的说Object就是所有类的超类),所以如果你有一个实现接口的类,你需要为这个方法提供实现。 但是这里需要记住一点。

无论如何,如果你没有在接口中声明这些方法,那么对于实现接口的子类来说,你仍然有这样的行为。

所以如果不声明它,它仍然会存在,另一件事是因为这些方法和Object类的其他方法都存在于类的所有对象中,所以不需要实现。