equals() を常にオーバーライドする必要がある場合、オブジェクト比較を行う必要に追い込まれないようにするための適切なアプローチは何ですか?
あなたは間違っています。equals はできるだけオーバーライドしないでください。
この情報はすべて、Effective Java, Second Edition ( Josh Bloch ) からのものです。これに関する初版の章は、今でも無料でダウンロードできます。
効果的な Java から:
問題を回避する最も簡単な方法は、equals メソッドをオーバーライドしないことです。この場合、クラスの各インスタンスはそれ自体とのみ等しくなります。
equals/hashCode を任意にオーバーライドすることの問題は、継承です。一部の equals 実装では、次のようにテストすることを推奨しています。
if (this.getClass() != other.getClass()) {
return false; //inequal
}
実際、Eclipse (3.4) Java エディターは、ソース ツールを使用してメソッドを生成するときにこれを行います。ブロッホによれば、これはリスコフの置換原理に違反しているため誤りです。
効果的な Java から:
equals コントラクトを維持しながら、インスタンス化可能なクラスを拡張して値コンポーネントを追加する方法はありません。
クラスとインターフェースの章では、等価性の問題を最小限に抑える 2 つの方法について説明します。
- 継承よりも構成を優先する
- 継承またはそれを禁止するための設計および文書化
私が見る限り、唯一の代替手段は、クラスの外部のフォームで等価性をテストすることであり、それがどのように実行されるかは、型の設計とそれを使用しようとしていたコンテキストによって異なります。
たとえば、比較方法を文書化するインターフェースを定義できます。以下のコードでは、Service インスタンスが実行時に同じクラスの新しいバージョンに置き換えられる可能性があります。その場合、異なる ClassLoaders を使用すると、equals 比較は常に false を返すため、equals/hashCode のオーバーライドは冗長になります。
public class Services {
private static Map<String, Service> SERVICES = new HashMap<String, Service>();
static interface Service {
/** Services with the same name are considered equivalent */
public String getName();
}
public static synchronized void installService(Service service) {
SERVICES.put(service.getName(), service);
}
public static synchronized Service lookup(String name) {
return SERVICES.get(name);
}
}
「なぜ 2 つのオブジェクトを比較したいのですか?」
明白な例は、2 つの Strings (または 2 つのFilesまたはURI ) が同じかどうかをテストすることです。たとえば、解析する一連のファイルを構築したい場合はどうでしょう。定義上、セットには一意の要素のみが含まれます。Java のSet型は equals/hashCode メソッドに依存して、その要素の一意性を強制します。