59

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

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

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

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

4

12 回答 12

45

听起来您想强制您的类覆盖这些方法的默认实现。如果是这样,这样做的方法是声明一个抽象超类,该超类具有声明为抽象的方法。例如:

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 节)。”

于 2009-11-11T21:22:12.083 回答
42

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

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

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

于 2009-11-11T21:16:08.503 回答
16

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

于 2009-11-11T21:16:07.963 回答
7

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

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

于 2009-11-11T21:17:05.917 回答
6

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

于 2009-11-11T21:22:16.733 回答
5

这些方法的实现一直来自Object.

于 2009-11-11T21:16:08.940 回答
4

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

于 2009-11-11T21:17:28.407 回答
4

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

不过,这可能会破坏首先使用接口的目的。这就是一些人建议永远不应该在 Object 类中包含 equals、toString 和 hashCode 的原因之一。

于 2009-11-11T21:21:47.890 回答
3

Java 只关心方法是在某处定义的。接口不会强制您重新定义第一次从接口继承的新类中的方法(如果它们已经定义)。由于java.lang.Object已经实现了这些方法,因此您的新对象符合接口,即使它们自己没有覆盖这三个方法。

于 2009-11-11T21:18:09.700 回答
1

Adam 告诉你为什么你无法摆脱尝试强制 equals、hashCode 和 toString 的原因。我会选择以下实现,它涉及 Adam 和 Stephan 提供的解决方案:

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() {
        ...
    }
}
于 2013-02-03T19:48:12.760 回答
0

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

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

于 2013-04-12T16:46:41.837 回答
-2

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

无论如何,如果您没有在接口中声明这些方法,那么首先实现该接口的子类仍然具有这种行为。

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

于 2009-11-11T21:46:25.647 回答