1. なぜ、equals() メソッドを書き直す必要があるのでしょうか?
2 つのオブジェクトが論理的に等しいかどうかを判断します。たとえば、2 つのクラスのインスタンスがクラスのメンバー変数に基づいて等しいかどうかを判断します。ただし、継承された Object の equals メソッドは、2 つの参照変数が同じオブジェクトであるかどうかを判断することしかできません。このように、多くの場合、equals() メソッドをオーバーライドする必要があります。
重複オブジェクトのないコレクションに要素を追加する場合、多くの場合、コレクション内に既知のオブジェクトがあるかどうかを最初に判断する必要があるため、equals メソッドをオーバーライドする必要があります。
2.equals() メソッドを書き直すには?
equals メソッドをオーバーライドするための要件:
1. 再帰性: x.equals(x) は、null 以外の参照 x に対して true を返す必要があります。
2. 対称性: x と y を参照する場合、x.equals(y) が true を返す場合、y.equals(x) も true を返す必要があります。
3. 推移性: 任意の参照 x、y、z について、x.equals(y) が true を返し、y.equals(z) が true を返す場合、x.equals(z) も true を返す必要があります。
4. 一貫性: x と y によって参照されるオブジェクトが変更されていない場合、x.equals(y) を繰り返し呼び出しても同じ結果が返されるはずです。
5. 非 null 可能性: null 以外の参照 x に対して、x.equals(null) は false を返す必要があります。
形式:
Javaコード
public boolean equals(Object obj) { if(this == obj) return false; if(obj == null) return false; if(getClass() != obj.getClass() ) return false; MyClass other = (MyClass)obj; if(str1 == null) { if(obj.str1 != null) { return false; } }else if (!str1.equals(other.str1) ) return false; } if(var1 != other.var1) return false; return true; }
equalsメソッドを保持したままサブクラスに新しい機能が追加されると、より複雑になります。
次に、例を通して上記の契約を理解しましょう。まず、単純な変更不可能な 2 次元点クラスから始めます:
public class Point{ private final int x; private final int y; public Point(int x, int y){ this.x = x; this.y = y; } public boolean equals(Object o){ if(!(o instanceof Point)) return false; Point p = (Point)o; return p.x == x && p.y == y; } }
このクラスを拡張して点に色情報を追加するとします:
public class ColorPoint extends Point{ private Color color; public ColorPoint(int x, int y, Color color){ super(x, y); this.color = color; } //override equasl() public boolean equals(Object o){ if(!(o instanceof ColorPoint)) return false; ColorPoint cp = (ColorPoint)o; return super.equals(o) && cp.color==color; } }
true を返す場合にのみ、equals メソッドをオーバーライドします。実際のパラメータが同じ位置と色の別の色の点である場合のみ。しかし、この方法の問題は、通常の点と色付きの点を比較すると、またその逆の場合に、異なる結果が得られる可能性があることです:
public static void main(String[] args){ Point p = new Point(1, 2); ColorPoint cp = new ColorPoint(1, 2, Color.RED); System.out.println(p.equals(cp)); System.out.println(cp.eqauls(p)); }
実行結果:
true
false
このような結果は明らかに対称性に違反しています。この問題を修正するには、これを試してください: 「混合比較」を実行するときに ColorPoint.equals に色情報を無視させます:
public boolean equals(Object o){ if(!(o instanceof Point)) return false; //如果o是一个普通点,就忽略颜色信息 if(!(o instanceof ColorPoint)) return o.equals(this); //如果o是一个有色点,就做完整的比较 ColorPoint cp = (ColorPoint)o; return super.equals(o) && cp.color==color; }
このメソッドの結果はどうなるでしょうか?まずテストしてみましょう:
public static void main(String[] args){ ColorPoint p1 = new ColorPoint(1, 2, Color.RED); Point p2 = new Point(1, 2); ColorPoint p3 = new ColorPoint(1, 2, Color.BLUE); System.out.println(p1.equals(p2)); System.out.println(p2.equals(p1)); System.out.println(p2.equals(p3)); System.out.println(p1.eqauls(p3)); }
実行結果:
true
true
true
false
このメソッドは対称性を提供しますが、推移性が犠牲になります (慣例により、p1.equals(p2) と p2.equals(p3) はすべてtrue を返す場合、p1.equals(p3) も true を返す必要があります)。どうやって解決すればいいでしょうか?
実は、これはオブジェクト指向言語における同値関係に関する基本的な質問です。インスタンス化可能なクラスを拡張して、イコール規約を維持しながら新しい機能を追加する簡単な方法はありません。新しい解決策は、ColorPoint に Point を拡張させるのではなく、プライベート Point フィールドとパブリック ビュー メソッドを ColorPoint に追加することです:
public class ColorPoint{ private Point point; private Color color; public ColorPoint(int x, int y, Color color){ point = new Point(x, y); this.color = color; } //返回一个与该有色点在同一位置上的普通Point对象 public Point asPoint(){ return point; } public boolean equals(Object o){ if(o == this) return true; if(!(o instanceof ColorPoint)) return false; ColorPoint cp = (ColorPoint)o; return cp.point.equals(point)&& cp.color.equals(color); } }
もう 1 つの解決策は、新しい機能をサブクラスに追加できるように、Point を抽象クラスとして設計することです。等号規約に違反せずに抽象クラスを作成します。抽象クラスはクラスのインスタンスを作成できないため、上記の問題は発生しません。
equals メソッドをオーバーライドする重要なポイント:
1. == 演算子を使用して、「実際のパラメーターがオブジェクトへの参照であるかどうか」を確認します。
2. 実パラメータが null かどうかを確認します
3. 実パラメータが正しい型であるかどうかを確認するには、instanceof 演算子を使用します。
4. 実際のパラメータを正しい型に変換します。
5. このクラスの各「キー」フィールドについて、実際のパラメータのフィールドが現在のオブジェクトの対応するフィールド値と一致するかどうかを確認します。 float 型でも double 型でもない基本型のフィールドの場合は、比較に == 演算子を使用できます。オブジェクト参照型のフィールドの場合は、参照されるオブジェクトの equals メソッドを再帰的に呼び出すことができます。float 型のフィールドの場合は、最初に Float を使用します。 .floatToIntBits を使用して int 型の値に変換します。次に、== 演算子を使用して int 型の値を比較します。double 型フィールドの場合は、まず
を使用して Double.doubleToLongBits を long 型の値に変換し、次に == 演算子を使用します。 long 型の値を比較します。
6.equals メソッドを作成したら、次の 3 つの質問を自問する必要があります。それは対称的か、推移的か、一貫性があるか? (他の 2 つの特性は通常、それ自体で満たされます) 答えが「いいえ」の場合は、これらの特性が満たされない理由を見つけて、equals メソッドのコードを変更してください。