web-dev-qa-db-ja.com

クラスに.equalsメソッドをオーバーライドさせる

共通のインターフェースを実装するクラスがたくさんあります:コマンド。

そして、このクラスの束はマップに行きます。

マップを正しく機能させるには、コマンドを実装する各クラスにObject.equals(Object other)メソッドをオーバーライドする必要があります。

大丈夫だよ。

しかし、私は平等のオーバーライドを強制したいと思います。 =>コマンドを実装するものがequalsをオーバーライドしない場合、コンパイルエラーが発生します。

それは可能ですか?

編集:ところで、ハッシュコードのオーバーライドを強制する必要もあります...

51
Antoine Claval

いいえ、できません。ただし、できることは、インターフェイスの代わりに抽象基本クラスを使用して、equals()を抽象化することです。

abstract class Command {
   // put other methods from Command interface here

   public abstract boolean equals(Object other);
   public abstract int hashCode();
}

Commandmustのサブクラスは、独自のequalsメソッドとhashCodeメソッドを提供します。

APIユーザーに基本クラスの拡張を強制することは一般的に悪い習慣ですが、この場合は正当化される可能性があります。また、コマンドインターフェイスにadditionで人工的な基本クラスを導入するのではなく、Commandをインターフェイスではなく抽象基本クラスにすると、APIユーザーが取得するリスクはありません。それは間違っています。

82
skaffman

Java.lang.Objectではなく、抽象XObjectからオブジェクトを拡張できますか?

public abstract class XObject
 extends Object
{
@Override
public abstract boolean equals(Object o);
}
17
Pierre

孫がいる場合、その父親がすでにequalsメソッドとhashCodeメソッドの両方をオーバーライドしているため、抽象クラスは機能しません。その後、問題が再び発生します。

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

4

これは、Commandがインターフェイス、または抽象クラスである場合にのみ可能です。ここで、equals(..)は抽象として宣言されたメソッドです。

問題は、すべてのオブジェクトのスーパークラスであるObjectがすでにこのメソッドを定義していることです。

これが(実行時に)問題であることを示したい場合は、例外をスローして、APIのユーザーにそれをオーバーライドさせることができます。しかし、少なくとも私の知る限り、コンパイル時には不可能です。

API固有のメソッドを使用して、回避してみてください。 CommandEquals。もう1つのオプションは、(前述のように)Equals抽象メソッドを定義する別のクラスを拡張することです。

1
NT_

ランタイムチェックが必要な場合は、次のようなことができます。

    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、2番目はfalseを出力します。コンパイル時間が必要な場合は、アノテーションを作成し、アノテーション処理ツールを使用して、すべてのアノテーション付きクラスが等しいことをオーバーライドすることを確認するのが唯一のオプションのように見えます。

1
Nikolay Ivanov

_interface Command_にboolean myEquals()を作成し、次のようにアダプタを作成できます。

_class MyAdapter{
  Command c;
  boolean equals(Object x) {
    return c.myEquals((Command)x);
  }
}
_

次に、map.put(key, new MyAdapter(command))の代わりにmap.put(key, command)を使用します

1
Nikita Prokopov
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.");
    }
}
1
Davide Consonni

equals()Objectから継承されているので、それを強制できないとは思えません。everyタイプの場合、equals()の自動継承実装があるからです利用可能です。

0
Joey

問題のマップに独自のJava.util.comparatorを提供することは可能ですか?

0
DerMike

Objectクラスからのものであるため、equalsを強制的にオーバーライドすることはできないと思います。

関連する注意点として、equalsをオーバーライドする場合は常に、Objectクラスの 'hashCode'メソッドをオーバーライドする必要があります。これは、クラスのインスタンスをマップのキーとして使用する場合に特に重要になります。この記事を確認してください: http://www.artima.com/lejava/articles/equality.html これは、equalsを正しい方法でオーバーライドする方法に関するヒントを提供します。

0
sateesh

他の答えがすでに説明しているように、あなたはあなたがしようとしているようなことを強制することはできません。

「十分に」機能する可能性のあるものの1つは、2番目のインターフェースを定義することです。これをMappableCommandと呼びます。

public interface MappableCommand 
{

}

このインターフェイスのドキュメントでは、クラス設計者が指定した要件を考慮した場合にのみ、クラスがこの(空の)インターフェイスを実装する必要があることを示しています。

次に、マップの値タイプをMappableCommandに設定すると、MappableCommandsのみをマップに追加できます。

これは、Javaのデフォルトのシリアル化メカニズムでシリアル化できるクラスに(空白の)インターフェースSerializableを実装する必要がある理由の背後にあるロジックに似ています。

これが機能しない場合は、実行時エラーをスローすることで解決する必要があります。

偽の編集:

この要件をより明確にしたい場合は、この方法で新しいインターフェイスを定義できます。

public interface MappableCommand 
{

    public void iOverrodeTheEqualsMethod();

    public void seriouslyIPromiseThatIOverrodeIt();

}
0
Tom Neyland

提案されている他のソリューションのバリエーションを次に示します。

public abstract class CommandOverridingEquals implements Command {
    public abstract boolean equals(Object other);
    public abstract int hashcode();
}

Map<String, CommandOverridingEquals> map = 
    new HashMap<String, CommandOverridingEquals>();

または、本当に確認したい場合は、チェック済みのハッシュマップを使用してください。例えば.

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

こういうのはやっぱり面倒なことになると思いがちです。たとえば、他の基本クラスを拡張し、(偶然に)equalshashcodeを所定の方法でオーバーライドする既存のコマンドクラスがたくさんあるとします。問題は、それらのクラスを使用できないことです。その代わり、私はそれらを再実装するか、ラッパーの束を書くことを強いられます。

IMO、開発者を特定の実装パターンに強制しようとするのは悪い考えです。 Javadocにいくつかの強力な警告を入れて、開発者に正しいことをするに頼るほうがよいでしょう。

0
Stephen C