私は単純な基本クラスを持っていますが、これは後で多くの個別のクラスによって拡張され、新しいフィールドを導入する可能性がありますが、必ずしもそうとは限りません。基本クラスでequalsメソッドを定義しましたが、いくつかのサブクラスではそれをオーバーライドしました。ベース/サブクラスで定義を混在させても大丈夫ですか?私の場合、同じフィールドをチェックするコードの重複を避けるためでした。
8 に答える
アンジェリカ・ランガーの「混合型比較を可能にするためのequals()の実装」をご覧ください。
ここにいくつかの問題と可能な解決策の簡単な説明があります:
等しい契約は(とりわけ)言う:
これは対称的です。null以外の参照値xおよびyの場合、y.equals(x)がtrueを返す場合に限り、x.equals(y)はtrueを返す必要があります。
つまり、サブクラスが新しいフィールドを導入していて、基本クラスのオブジェクト(またはequalsをオーバーライドしない別のサブクラス)をこのサブクラスのオブジェクトと比較している場合、問題が発生する可能性があります。
次のことは行わないでください。
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BaseClass) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class BadSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof BadSubClass) {
return super.equals(obj)
&& field2 == ((BadSubClass) obj).field2;
}
return false;
}
}
あなたが得るので
BaseClass baseClass = new BaseClass();
BadSubClass subClass = new BadSubClass();
System.out.println(baseClass.equals(subClass)); // prints 'true'
System.out.println(subClass.equals(baseClass)); // prints 'false'
考えられる解決策:
instanceof
-checkをクラス比較に置き換えます。
obj != null && obj.getClass() == getClass()
このソリューションでは、BaseClass
のオブジェクトがサブクラスのオブジェクトと等しくなることはありません。
SubClass
メソッドを使用せずに@Override
別のオブジェクトを作成する場合equals
、2つのSubClass
-objectは(BaseClass.equals
チェックでそう判断された場合)すぐに互いに等しくなりますが、SubClass
-objectが-objectと等しくなることはありませんBaseClass
。
適切な実装は次のとおりです。
class BaseClass {
private int field1 = 0;
@Override
public boolean equals(Object obj) {
if (obj != null && obj.getClass() == getClass()) {
return field1 == ((BaseClass) obj).field1;
}
return false;
}
}
class GoodSubClass extends BaseClass {
private int field2 = 0;
@Override
public boolean equals(Object obj) {
if (obj instanceof GoodSubClass) {
return super.equals(obj) && field2 == ((GoodSubClass) obj).field2;
}
return false;
}
}
より高度な問題とその解決策については、上記の記事を参照してください。
いいえ、equalsメソッドに関連する新しいフィールドを導入する場合、equalsコントラクトに準拠することはできません。詳細については、JoshuaBlochによる「EffectiveJava」を参照してください。
編集:
現在、本は手元にありませんが、基本クラスが抽象的/インスタンス化できない場合は問題ないと思います。
以下はすべてのケースを処理するわけではありませんが、非常に実用的であることがわかりました。スーパークラスとサブクラスの両方を使用しているときに、これを何度も使用しました。それらを相互比較したくはありませんが、SubClassのすべてのSuperClass equals()を再実装したくありません。それは処理します:
- a.equals(b)== b.equals(a)
- フィールド比較コードを複製しません
- 任意のサブクラスの深さに対して簡単に一般化
- Subclass.equals(SuperClass)== false
- Superclass.equals(SubClass)== false
コード例
// implement both strict and asymmetric equality
class SuperClass {
public int f1;
public boolean looseEquals(Object o) {
if (!(o instanceof SuperClass)) return false;
SuperClass other = (SuperClass)o;
return f1 == other.f1;
}
@Override public boolean equals(Object o) {
return looseEquals(o) && this.getClass() == o.getClass();
}
}
class SubClass extends SuperClass {
public int f2;
@Override public boolean looseEquals(Object o) {
if (!super.looseEquals(o)) return false;
if (!(o instanceof SubClass)) return false;
SubClass other = (SubClass)o;
return f2 == other.f2;
}
// no need to override equals()
}
このメソッドを使用して、super()
拡張しているクラスのメソッドを呼び出して、コードの重複の必要性を防ぐことができます。
public class BaseClass {
public boolean equals(BaseClass other) {
return (other.getBlahblah() == this.Blahblah && .....);
}
}
public class DerivedClass extends BaseClass {
public boolean equals(DerivedClass other) {
return (super(other) && other.getNewAttribute() == this.NewAttribute.....);
}
}
非常に有効なアプローチです。問題はサブクラスの1つにあり、親によってバインドされているequalsの定義を保持する必要があります。そうしないと、equals関数が壊れてしまい、実行時に非常にユニークなシナリオが発生する可能性があります。
私は、それはequals(Object obj)
、hashCode()
メソッドの実装super
class
を提供するのに最適だと思いJava
ます。hashCode() and equals(Object obj)
Javaが基本クラスjava.lang.Objectでメソッド実装を提供することは誰もが知っていますがoverride
、必要な場合はいつでもclass
。
コードを正しく記述しないと、非対称性(平等の契約に違反する)と呼ばれる深刻な問題が発生するため、オプションを見てみましょう。
ベストプラクティス–同じクラスの戦略。BがAのサブクラスであり、各クラスに独自のequalsメソッドがあり、同じクラス戦略を使用して実装されている場合、Bの将来のサブクラスにequalsの非対称定義が導入されないように、クラスBをfinalとして宣言する必要があります。
質問。Bをファイナルにしたくない場合はどうなりますか?
継承の代わりに構成を使用します。クラスBとA(BはAのサブクラス)が異なるequalsメソッドを必要とする場合は常に、継承の代わりに合成を使用するのが良い戦略であり、クラスBをfinalにすることがオプションでない場合は、equalsを処理する唯一の安全な方法です。 。
どのように?
public class A{
public boolean equals(Object ob){
//write your code here
}
}
class B{
A a= new A();
public B(A a){
this.a= a;
}
public boolean equals(Object ob){
//...write your code here
if(!((B)ob).a).equals(a)) return false;
//...write your code here
}
}
eqauls()とhashcode()のコントラクトに従う限り、それは完全に問題ないと思います。