equals
メソッドを問題なくオーバライドすることは簡単ではありません。
問題の発生を防ぐためには equals
メソッドをオーバライドしないのが一番です。
この場合は、equals
は自分自身と同じオブジェクトである場合にのみ true
を返すように振る舞います。
public boolean equals(Object o) {
return this == o;
}
振る舞いを定義するようなクラスでは、上記のようなデフォルトの equals
実装で十分なことがほとんどです。
一方で、Integer や Date クラスのような論理的な値を表すクラスでは、論理的な同値判断を行えることを期待するでしょう。
equals
メソッドをオーバライドする場合は、下記のような条件を満たすことが求められます(Object クラスの説明抜粋)。
@Override public boolean equals(Object o) {
...
}
自分自身のクラスの型をパラメータにとる equals
メソッドを定義(オーバロード)することは考えないほうがよいです。
結局は Object
を引数にとる equals
の定義も必要であり、これらの整合性をとりながら更新することは容易ではありません。
@Override
アノテーションを付けて、親クラスの equals
をオーバライドしていることを明示しましょう。
この比較は必須ではありませんが、同じオブジェクト同士を比較する頻度が高い場合は、実行時のパフォーマンスの向上を見込めます。
@Override public boolean equals(Object o) {
if (o == this) {
return true;
}
...
}
equals
メソッドをオーバライドする目的は、そのクラス独自のフィールドの同値性を確認したいからなので、まずは渡されたオブジェクトが論理的に等しい型のオブジェクトであることをチェックします。
多くの場合は、自分自身のクラスの型と同じであるかを調べればよいのですが、このクラスがあるインタフェースを実現するための具象クラスである場合は、等しいインタフェースを実装したオブジェクトが渡されたかどうかを調べるということもあり得ます(この場合も insntanceof
で OK)。
@Override public boolean equals(Object o) {
...
if (!(o instanceof DerivedClass)) {
return false;
}
...
}
なお、この instanceof
はオブジェクトが null
のときには false
を返すように動作するため、null チェックの用途も兼ねています。
equals
の実装において、明示的な null
チェックを行う必要はありません(冗長です)。
前のステップの instanceof
によって、より具体的な型にダウンキャストできることが保証されています。
@Override public boolean equals(Object o) {
...
DerivedClass d = (DerivedClass) o;
return d.field1 == field1 && d.field2 == field2;
}
実行時のパフォーマンスを考慮して、フィールドの比較順序を決める必要があります。
値が異なる可能性の高いフィールドを優先的に比較することで、equals
メソッドの処理が早く終了することを期待できます。
上記の例では、field1
の比較を field2
の比較より先に行っていますが、これは、field1
が不一致になる可能性が、field2
が不一致になる可能性よりも高いことを考慮した実装です。
単純にアルファベット順に並べているわけではないことに注意してください。
equals
メソッドをオーバライドした場合は、必ず hashCode
メソッドもオーバライドする必要があります。
→ equals をオーバーライドする時は、常に hashCode をオーバーライドする
ここまでのチェックポイントを整理すると、コードは下記のような感じになります。
@Override public boolean equals(Object o) {
if (o == this) {
return true;
}
if (!(o instanceof DerivedClass)) {
return false;
}
DerivedClass d = (DerivedClass) o;
return d.field1 == field1 && d.field2 == field2;
}
@Override public int hashCode() {
...
}