Effective Java を再度読み直して感想や考察を書いています。
今回は項目8「equals をオーバーライドする時は一般契約に従う」を読み直してみました。
equals の一般契約
「一般契約」というと大げさですが単なるお約束。
equals のお約束は次の5つ。簡単に説明します。
反射的
x.equals(x); // → true になること。
当たり前ですね。
これが false になるソースを見たら、金魚のように口をパクパクさせるしかありません。
対称的
x.equals(y); // → true の場合 y.equals(x); // → true になること。
継承を使った場合、やらかしやすい。
書籍の中では例を交え間違いの例を紹介しています。
推移的
x.equals(y); // → true 且つ、 y.equals(z); // → true の場合、 x.equals(z); // → true になること。
整合的
要するにインスタンスの値に変更がなければ、x.equals(y) は常に同じ値を返す必要があるということです。
整合的「ではない」例として java.net.URL が紹介されていました。なんとこの URL 、equals を呼ぶとネットワーク越しに IP アドレスを取得しに行くそうです。当然ネットワークは不安定なので、複数回 x.equals(y) を呼び出すとインスタンスの中身をまったく替えていないのにもかかわらず、戻り値が異なる可能性があります。
Java の API を開発するような天才プログラマでもやらかすことはあるのですね。
非 null 性
オブジェクトは null と等しくてはいけない。x.equals(null) は必ず false になる必要があります。
その他 Tips
equals メソッドでは null チェック不要
@Override public boolean equals(Object o) { if (o == null) return false; // ← は不要 if (!(o instanceof Human)) return false; Human h = (Human) o; return name.equals(h.getName()); }
上記で最初に行っている null チェックは不要です。o が null の場合、instanceof が false を返すからです。
java.sql.Timestamp は 一般契約を守っていない
対称的のところで「継承を使った場合、やらかしやすい」と書きましたが Timestamp がまさにそれです。
Timestamp は java.util.Date を継承してナノ秒まで保持できるように拡張されています。そして Date のインスタンスと equals を使って比較すると対照的でなくなります。
簡単な例です。
public static void main(String[] args) { Date d = new Date(); Timestamp t = new Timestamp(d.getTime()); System.out.println(d.equals(t)); // true System.out.println(t.equals(d)); // false }
対照的でないので、同一コレクションで Date と Timdestamp それぞれのインスタンスを保持すると不整合が起きます。注意しましょう。
継承はむずかしい
前述のとおり、継承は相当注意しないと間違いを起こしやすいです。特別な事情がない限り、Effective Java 項目16 にもあるとおり「継承よりコンポジションを選ぶ」方が良いでしょう。
Effective Java 項目8のまとめ
equals の正しいオーバーライド方法が学べます。最近ではエディタが equals を自動生成してくれたりもしますが、クラスの意図に合わせ適切に修正できるように基本は抑えておきましょう。
それでは!
- 作者: Joshua Bloch,柴田芳樹
- 出版社/メーカー: 丸善出版
- 発売日: 2018/10/30
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
- 作者: 中山清喬,国本大悟
- 出版社/メーカー: インプレス
- 発売日: 2014/08/07
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (19件) を見る
- 作者: 結城浩
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2004/06/19
- メディア: 大型本
- 購入: 51人 クリック: 762回
- この商品を含むブログ (397件) を見る