web-dev-qa-db-ja.com

インターフェイスのtoString()、equals()、hashCode()

そのため、実装する必要のある多数のメソッドとのインターフェイスがあり、メソッド名は無関係です。

このインターフェイスを実装するオブジェクトは多くの場合コレクションに入れられ、使用したい特別なtoString()形式も持っています。

したがって、hashCode()、equals()、およびtoString()をインターフェイスに配置して、これらのデフォルトメソッドをオーバーライドすることを忘れないようにするのが便利だと思いました。しかし、これらのメソッドをインターフェイスに追加したとき、明示的にインターフェイスに入れたとしても、これら3つのメソッドを実装していない場合、IDE /コンパイラは文句を言いません。

なぜこれが強制されないのですか?他のメソッドを実装しないと文句を言いますが、これらの3つを強制しません。何が得られますか?手がかりはありますか?

51
J_Y_C

Javaから継承するすべてのオブジェクトはJava.lang.Objectから継承し、 Object はこれらのメソッドのデフォルト実装を提供します。

インターフェイスに他のメソッドが含まれている場合、これらのメソッドの実装を提供してインターフェイスを完全に実装しないと、Javaが文句を言います。ただし、equals()hashCode()およびtoString()(および言及しなかった他のいくつかと同様)実装は既に存在します。

希望することを達成できる方法の1つは、インターフェイスに別のメソッド、たとえばtoPrettyString()などを提供することです。次に、デフォルトのtoString()メソッドの代わりにそのメソッドを呼び出すことができます。

38
Adam Batkin

これらのメソッドのデフォルト実装をオーバーライドするために、クラスをforceしたいようです。その場合、これを行う方法は、抽象として宣言されたメソッドを持つ抽象スーパークラスを宣言することです。例えば:

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

    public abstract boolean equals(Object other);

    public abstract int hashCode();

    public abstract String toString();
}
_

次に、現在のクラスをこのクラスextendに変更します。

このアプローチは一種の働きをしますが、理想的な解決策ではありません。

  • 既存のクラス階層では問題になる場合があります。

  • 既存のインターフェースを実装して特定の抽象クラスを拡張するクラスをforceすることは悪い考えです。たとえば、メソッドシグネチャのパラメーターを変更して、既存のインターフェイスではなく抽象クラスを使用できます。しかし、最終結果は柔軟性の低いコードになります。 (そして、とにかくこれを破壊する方法を見つけることができます;例えば、super.<method>(...)呼び出しでメソッドを「実装する」独自の抽象サブクラスを追加することによって!)

  • 特定のクラス階層/実装パターンを課すことは近視眼的です。将来の要件の変更によって制限が困難になることを意味するかどうかを予測することはできません。 (これが、特定のクラスではなくインターフェイスに対してプログラミングを推奨する理由です。)


インターフェースがクラスにこれらのメソッドの再宣言を強制しない理由に関する実際の質問に戻ります。

なぜこれが強制されないのですか?他のメソッドを実装しないと文句を言いますが、これらの3つを強制しません。何が得られますか?手がかりはありますか?

インターフェースは、それを実装する具象クラスが各メソッドの実装を持つという制約を課します。ただし、クラスitselfがこれらのメソッドを実装する必要はありません。メソッドの実装は、スーパークラスから継承できます。そしてこの場合、それが起こっていることです。 _Java.lang.Object_から継承されたメソッドは、制約を安全にします。

JLS 8.1.5 は次のように述べています。

」宣言されているクラスが抽象クラスでない限り、各クラスの宣言によって、各直接スーパーインターフェイスのすべての抽象メンバーメソッドを実装する必要があります(§8.4.8.1)または、直接のスーパークラスまたは直接のスーパーインターフェースから継承された既存のメソッド宣言によって、抽象ではないクラスは抽象メソッドを持つことが許可されていないため(§8.1.1.1)。

39
Stephen C

これらの3つのメソッドはすべて Java.lang.Object これは、他のすべてのクラスによって(暗黙的に)拡張されます。したがって、これらのメソッドのデフォルトの実装が存在し、コンパイラーは文句を言う必要がありません。

17
ChssPly76

インターフェイスを実装するクラスは、Objectも拡張します。オブジェクトは、hashCode、equals、およびtoStringを定義し、3つすべてのデフォルト実装を持っています。

あなたが達成しようとしているのは良いことですが、実際的ではありません。

7
Steve McLeod

Objectからのすべての方法でこれらのメソッドの実装があります。

5
Tordek

Equals()およびhashCode()を強制的にオーバーライドする場合は、これらのメソッドを抽象として定義する抽象スーパークラスから拡張します。

5
Bozho

すべてのオブジェクトは、オーバーライドされない限り、Objectからこれらのメソッドを継承するため、オブジェクトにはこれらの3つのメソッドの実装が既に含まれています。

4
Matt Ball

Javaは、メソッドがどこかで定義されていることだけを気にします。既に定義されている場合、インターフェイスは、インターフェイスから初めて継承する新しいクラスのメソッドを再定義することを強制しません。 Java.lang.Objectはすでにこれらのメソッドを実装しているため、これらの3つのメソッドを独自にオーバーライドしなくても、新しいオブジェクトはインターフェイスに準拠します。

3
Ken Bloom

他の人があなたの実際の質問に十分に答えています。特定の問題の解決策としては、独自のメソッド(getStringRepresentation、getCustomHashcode、equalsObjectなど)を作成し、オブジェクトに、equals、toString、hashCodeメソッドがこれらのメソッドを呼び出す基本クラスを拡張させることを検討できます。

ただし、そもそもインターフェイスを使用する目的を損なう可能性があります。これは、そもそも、equals、toString、およびhashCodeをObjectクラスに含めるべきではないと示唆した人がいる理由の1つです。

3

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

現在、明確に区別され、文字列として表されることが必要なクラスは、checkEquals、hashCode、およびtoStringの実装を強制するDistinctStringableObjectを拡張できます。

サンプルコンクリートクラス:

public abstract class MyDistinctStringableObject extends DistinctStringableObject {

    @Override
    public final boolean checkEquals(Object other) {
        ...
    }

    @Override
    public final int hash() {
        ...
    }

    @Override
    public final String asString() {
        ...
    }
}
1
Neel

父親が既にequalsメソッドとhashCodeメソッドの両方をオーバーライドしているため、孫がいる場合、抽象クラスは機能しません。

AnnotatinsとAPT( http://docs.Oracle.com/javase/1.5.0/docs/guide/apt/GettingStarted.html )を使用して取得してみてくださいできた。

0