Upload
appresso-engineering-team
View
491
Download
4
Embed Size (px)
DESCRIPTION
3章 項目8,9,10
Citation preview
Effective Java 輪読会 第 2 回(項目 8 ~ 10 )
2013/12/04
開発部 野口
項目 8 equalsをオーバーライドする時は一般契約に従う
一般契約• 一般契約に従うことは、それらのメソッ
ドをオーバーライドするクラスの責任
equals をオーバーライドしないのが正しいとき (1/2)
• クラスの個々のインスタンスは、本質的に一意である– 例) Thread クラス
• 「論理的等価性」検査を、クラスが提供するかどうかに関心がない– 例) java.util.Random クラス
equals をオーバーライドしないのが正しいとき (2/2)
• スーパークラスがすでに equals をオーバーライドしており、スーパークラスの振る舞いがこのクラスに対して適切である– 例) AbstractSet とほとんどの Set 、 AbstractList
と List 、 AbstractMap と Map• クラスが private あるいはパッケージプライ
ベートであり、その equals メソッドが決して呼び出されないことが確かである– その場合、呼び出されないことをアサートすべき
equals をオーバーライドするのが適切なとき
• クラスが論理的等価性という概念を持っている
• スーパークラスが(そのクラスが必要とする振る舞いを実装するために) equals をオーバーライドしていない
• 一般に、値クラスの場合– 例) Integer 、 Date
enum の equals はオーバーライドする必要がない
• 個々の値に対してたかだか 1 つのオブジェクトしか存在しないため– 論理的等価性とオブジェクトの同一性が一致
する
equals メソッドの一般契約• 反射的• 対称的• 推移的• 整合的• 非 null
反射性• 普通に実装すれば自然とそうなります
対称性• 簡単に破ってしまうことがある!– pp.35- String と CaseInsensitiveString の例
推移性• これも簡単に破ってしまうことがある!– pp.36- Point と ColorPoint の例
★ インスタンス化可能なクラスを拡張して、 equals 契約を守ったまま値要素を追加する方法はない →じゃあどうするの?
推移性を保つ• instanceof のかわりに getClass を用いる?– LSP 違反!( pp.38- CounterPoint の例)
• 継承ではなくコンポジションを用いる– 解決( pp.39- ColorPoint の例)– See also: 項目 16 継承よりコンポジションを選び
なさい
★ リスコフの置換原則( Liscov Substitution Principle: LSP ) 「派生型はその基本型と交換可能でなければならない」
注意• java.sql.Timestamp は対称性を守っていない
(悪い)例!– ドキュメントに注意書きもあるが、誤用して
もコンパイルエラーにはならない• デバッグが困難になる
• 抽象クラスの継承においては、これらの問題は起こらない– See also: 項目 20 タグ付クラスよりクラス階
層を選ぶ
整合性• 不変オブジェクトの equals は常に一貫性
のある値を返すようにする– 疑問:「意図せず、そうではなくなってしま
う」ケースがあるか? →むしろ「不変オブジェクトにして、整合性
を確保することを検討すべき」ということが主眼
→「信頼できないリソース」に依存するとそうなりうる!
非 null 性• null との比較で NullPointerException をス
ローしない• かわりに false を返す• 明示的な null チェックは不要
で、 instanceof で十分
まとめ:高品質な equals のための処方箋
• 自分自身かどうかを検査するために、まず == を用いる
• 引数の型の正しさを検査するために、 instanceof を用いる
• 引数を正しい型にキャストする• 「意味のある」フィールドについて、一致
を検査する• 対称性、推移性、整合性をテストする
補足説明• equals をオーバーライドするときは、常に
hashCode をオーバーライドする(項目 9 )• あまりにも賢くなろうとしない– 例) File クラスは、同じファイルを参照してい
るシンボリックリンクを等しいとは見なさない• equals 宣言中の Object を他の型で置き換え
ない– この問題を防ぐため、 @Override を用いよう
項目 9 equalsをオーバーライドするときは、常に hashCodeをオーバーライドする
hashCode メソッドの一般契約• アプリケーション実行中、 equals について
変化していないオブジェクトの hashCode は常に同じ整数を返す
• equals について等しい 2 つのオブジェクトの hashCode は同じ整数を返す
• equals について等しくない 2 つのオブジェクトの hashCode が別々の整数を返す必要はない– ただし、ハッシュテーブルのパフォーマンスに影響しうる
hashCode をオーバーライドするのを忘れた場合
• 「等しいオブジェクトは、同じハッシュコードを持つ」という契約が破られる
• equals で等しくても、別のオブジェクトなので、デフォルトの hashCode は別々の値を返す– 例) pp.45- PhoneNumber クラス
hashCode の実装• (正当だが)最悪な実装
• 良い実装の簡単な処方箋– pp.47 を参照
★Eclipse には、項目 8 ~ 9 の指針に概ね沿った equalsと hashCode の自動生成機能があるようです - http://hysa.hateblo.jp/entry/20101108/1289223367
@Override public int hashCode() { return 42; }
hashCode の実装(補足)( 1/2 )
• 主にハッシュキーとして使用されるようなオブジェクトであれば、ハッシュコードのキャッシュを検討する– インスタンス生成時–遅延初期化
• 「パフォーマンス向上のため」として、意味のあるフィールドを計算から除外してはいけない–失敗例) Java のリリース 1.2 より古い String
hashCode の実装(補足)( 2/2 )
• hashCode の詳細を公開すべきではない–将来より良いハッシュ関数を適用することの妨げになる
項目 10 toStringを常にオーバーライドする
toString メソッドの一般契約• 簡潔だが、人が読みやすくなっている有益な表現
• すべてのサブクラスがこのメソッドをオーバーライドすることを推奨する
toString メソッドの実装• 実用的な場合には、オブジェクトに含ま
れる興味がある情報をすべて返す• すべて返すのが実用的でない場合、要約を
返す– それ自身で意味が自明であるのが理想• ○ Manhattan white pages (1487536 listings)• △ Thread[main,5,main]
toString メソッドのドキュメンテーション( 1/3 )
• 形式を明示する–○ 標準的で曖昧さがなく、可読性があるオブ
ジェクトの表現形式を提供できる– ○ String を引数とするファクトリメソッド/コ
ンストラクタを提供できる– ✕ 形式を変更できなくなる
toString メソッドのドキュメンテーション( 2/3 )
• 形式を明示しない–○ 形式を変更できる–△ 形式を明示する場合のメリット(○)は得
られない
toString メソッドのドキュメンテーション( 3/3 )
• 形式を明示するか否かにかかわらず、意図は明確にする
• 形式を明示するか否かにかかわらず、 toString に含まれる情報へのアクセサを提供する