equals() メソッドの規約の1つに、symmetry があります。
これは、a.equals(b) が true ならば b.equals(a) も true でなければならないというものです。
イメージし易そうでし辛いので、以下に例を示します。
例
以下のような、何らかのタイトルを意味するクラスがあるとします。
public class Title { private String name; public Title() { } public String getName() { return this.name; } public void setName(String name) { this.name = name; } }
この場合、文字列が等しいことのみを要件と考えてしまい、equals() メソッドを以下のように実装してしまうとどうでしょう。
public boolean equals(Object o) { if (o instanceof Title) { // 型が Title ならば、name が等しいか否かを検証する。 Title target = (Title) o; return this.name.equals(target.name); } if (o instanceof String) { // 型が String ならば、name と等しいか否かを検証する。 String target = (String) o; return this.name.equals(target); } // 上記以外は false! return false; }
(極端ですが、)このように実装してしまうと、以下のように symmetry を実現できなくなります。
Title title = new Title(); title.setName("Java"); String name = "Java"; ↓ title.equals(name) = true; name.equals(title) = false;
正しくは、次のように実装することとなります。
@Override public boolean equals(Object o) { // reflexivity の検証(パフォーマンス向上策を含む) if (o == this) { return true; } // non-nullity と型の検証 if (!(o instanceof Title)) { return false; } // instanceof で型の検証は済んでいるため、このキャストの動作は保障される。 Title target = (Title) o; // symmetry の検証 // name が null のケースがあるので、null のケースとそうではないケースとを分ける必要がある。 return (this.name == null ? target.name == null : this.name.equals(target.name)); }