Effective Javaを読むチャレンジ-項目8の1-

項目8 equalsをオーバーライドする時は一般契約に従う

equalsメソッドはjava.lang.Objectに定義されているメソッドで、final修飾子のない拡張可能なメソッドとなっています。Objectクラスのマニュアルにはこう書いてあります。

2つのオブジェクトの同値関係を保証することがequalsの一般契約です。じゃあ同値関係の保証とは、の前に、本書ではequalsをオーバーライドしなければならない場合に付いて述べています。そもそもequalsは必ずオーバライドするものでもないし、テストも大変、バグの元でもあります。ではequalsをオーバーライドしなかった場合はどうなるか、というと自分自身のインスタンスとだけ等しくなります。javaコードを見てみると、

というように、==演算子を使っています。==演算子は、等価演算子と呼ばれています。Java言語仕様を見ると、等価演算子は比較するオペランドの違いによって、3つの比較パターンに別れます。それは数値、真偽値、参照の場合です。

数値の場合はオペランドの数値昇格の結果によってその値を比較します。真偽値(booleanまたはBoolean)の場合は直感で分かると思いますが、trueかfalse値の比較です。そして参照の場合ですが、オペランドの値が同じオブジェクトまたは配列を参照している場合、==の結果はtrue、それ以外の結果はfalseとあります。この言語仕様により、等価演算子はプリミティブ型では同値性、参照型では同一性の比較をしているということが分かります。

Objectクラスが==演算子を使っているので、equalsをオーバーライドしなかった場合は自分自身のインスタンスとだけ等しくなる(参照が同じ)となるのですね。しかしながらObjectクラスのequalsメソッドが同値性でなく同一性の保証しかしていないのは、あらゆるクラスのスーパークラスだからでしょう。javaのクラスが全てデータを持っているなら話は別ですが。

それではどういった場合にequalsをオーバーライドするのか、という部分を読んでいきます。

オブジェクトが論理的等価性の概念に求められる振る舞いを実装するために、スーパークラスがequalsをオーバーライドしていない時

難しい表現ですがひとことでいうと、IntegerやDateなどの値クラスです。こういったオブジェクトは、クライアントがequalsメソッドを使ってそのオブジェクトが論理的に同値かどうかを比較することを期待しているからです。例えば日付クラスの場合、1月3日 ≠ 1月3日という結果になるならクライアントは誰も満足しないでしょう。

スーパークラスがequalsをオーバーライドしていない時というのは、スーパークラスのequalsメソッドがサブクラスでも適切に適用されるならば、オーバーライドする必要はないということです。

同値関係とは

いよいよ同値関係の中身の話です。マニュアルには、同値関係とは以下とあります。

  • 反射性(reflexive): null以外の参照値xについて、x.equals(x)はtrueを返します。
  • 対称性(symmetric): null以外の参照値xおよびyについて、y.equals(x)がtrueを返す場合に限り、x.equals(y)はtrueを返します。
  • 推移性(transitive): null以外の参照値x、y、およびzについて、x.equals(y)がtrueを返し、y.equals(z)がtrueを返す場合、x.equals(z)はtrueを返します。
  • 一貫性(consistent): null以外の参照値xおよびyについて、x.equals(y)の複数の呼出しは、このオブジェクトに対するequalsによる比較で使われた情報が変更されていなければ、一貫してtrueを返すか、一貫してfalseを返します。
  • null以外の参照値xについて、x.equals(null)はfalseを返します。

本書ではこの後10ページに渡ってこの同値関係の詳細について解説されています。その詳細を読むのは次回とします。

広告
  • LINEで送る