接口中的toString(),equals()和hashCode()

时间:2009-11-11 21:13:59

标签: java interface equals hashcode tostring

所以,我有一个接口,需要实现一堆方法,方法名称无关紧要。

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

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

为什么不对我强制执行?它抱怨如果我没有实现任何其他方法,但它没有强制执行这三个方法。是什么赋予了?有线索吗?

12 个答案:

答案 0 :(得分:39)

听起来您希望强制您的类来覆盖这些方法的默认实现。如果是这样,那么执行此操作的方法是声明一个抽象超类,该类具有声明为abstract的方法。例如:

public abstract class MyBaseClass implements ... /* existing interface(s) */ {

    public abstract boolean equals(Object other);

    public abstract int hashCode();

    public abstract String toString();
}

然后将当前班级更改为extend此课程。

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

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

  • 强制类实现现有接口以扩展特定的抽象类是一个坏主意。例如,您可以更改方法签名中的参数以使用抽象类而不是现有接口。但最终结果是代码灵活性较低。 (并且人们可以找到破坏它的方法;例如通过添加他们自己的抽象子类来“实现”super.<method>(...)调用方法!)

  • 强加一个特定的类层次结构/实现模式是短视的。您无法预测未来的需求变更是否意味着您的限制会导致困难。 (这就是人们建议针对接口而不是特定类进行编程的原因。)


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

  

为什么不对我强制执行?它抱怨如果我没有实现任何其他方法,但它没有强制执行这三个方法。是什么赋予了?有线索吗?

接口强加了一个约束,即实现它的具体类具有每个方法的实现。但是,它不要求类本身实现这些方法。方法实现可以从超类继承。在这种情况下,这就是正在发生的事情。从java.lang.Object继承的方法满足约束条件。

JLS 8.1.5声明如下:

  

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

答案 1 :(得分:38)

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

如果您的界面包含其他方法,如果您没有通过提供这些方法的实现来完全实现接口,Java会抱怨。但是对于equals()hashCode()toString()(以及您未提及的其他几个),实施已经存在。

您可以通过在界面中提供不同的方法(例如toPrettyString()或类似的方法)来实现您想要的一种方式。然后,您可以调用该方法而不是默认的toString()方法。

答案 2 :(得分:17)

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

答案 3 :(得分:7)

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

你想要达到的目标是好的,但不切实际。

答案 4 :(得分:5)

这些方法的实现从Object开始。

答案 5 :(得分:5)

如果要强制覆盖equals()和hashCode(),请从抽象超类扩展,该超类将这些方法定义为抽象。

答案 6 :(得分:4)

您的对象已包含这三种方法的实现,因为每个对象都从Object继承这些方法,除非它们被覆盖。

答案 7 :(得分:3)

Java只关心方法是在某处定义的。如果已经定义了接口,那么该接口不会强制您重新定义首次从接口继承的新类中的方法。由于java.lang.Object已经实现了这些方法,因此即使新对象不能单独覆盖这三种方法,它们也符合接口。

答案 8 :(得分:3)

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

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

答案 9 :(得分:1)

Adam告诉你为什么你不能试图强制使用equals,hashCode和toString。我会采取以下实施,触及亚当和斯蒂芬提供的解决方案:

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

答案 10 :(得分:0)

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

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

答案 11 :(得分:-2)

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

无论如何,如果你没有在接口中声明这些方法,你仍然会对首先实现接口的子类有这种行为。

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