Tbpgr Blog

Ruby プログラマ tbpgr(てぃーびー) のブログ

書籍 Effective Java

概要

書籍 Effective Javaに関するメモ。
全てをカバーせず、自分の中で新たに学ぶことやまとめ直しておきたいことのみを抜粋します。
また、例示するサンプルも写経せずに出来るだけ自分で考えたサンプルにします。

各章

1章 はじめに 略

書籍全般に関する説明

2章 オブジェクトの生成と消滅
項番 内容 詳細
1 コンストラクタの代わりにstaticファクトリーメソッドを検討する Refactoring to PatternsのReplace Constructors with Creation Methodと同じ話
2 数多くのコンストラクタパラメータに直面した時にはビルダーを検討する 詳細は個別記事参照
3 privateのコンストラクタenum型でシングルトン特性を矯正する シングルトンの実装のイディオム。Enumとして宣言し、列挙型の一つにインスタンスを設定することでシングルトンを保証する
4 privateのコンストラクタインスタンス化不可能を強制する Utilityなど、インスタンス化が不要な場合はコンストラクタを不可視にすること
5 不必要なオブジェクト生成を避ける ユーティリティなどで一度初期化すれば良い変数を静的初期化したり、ループ中のAutoBoxingによる大量の生成を避ける
6 廃れたオブジェクト参照を取り除く メモリリーク対応。適切なスコープでの変数の利用やnullの指定による参照の解放を行う
7 ファイナライザを避ける −−
3章 全てのオブジェクトに共通のメソッド
項番 内容 詳細
8 equalsをオーバーライドする時は一般契約に従う 詳細は個別記事参照
9 equalsをオーバーライドする時は常にhashCodeをオーバーライドする equalsメソッドが等しければ、hashメソッドも等しい必要がある
10 toStringを常にオーバーライドする println,assert,デバッガの利用時等toStringに有用な情報を設定しておくことで恩恵を得ることが出来る。
常にオーバーライドするのが理想
11 cloneを注意してオーバーライドする 詳細は個別記事参照
12 Comparableの実装を検討する 詳細は個別記事参照
4章 クラスとインターフェース
項番 内容 詳細
13 クラスとメンバーへのアクセス可能性を最小限にする 変更に強いプログラムを作るためにフィールドやメソッドはできる限り狭い範囲で公開すること
14 publicクラスでは、publicのフィールドではなく、アクセッサーメソッドを使う publicクラスのフィールドはアクセッサーメソッドを利用すること。

フィールドをそのまま公開すると変更不可能になり、柔軟性を損なう。

逆に内部の利用に限定されたインナークラスなどはフィールドを直接操作しても問題ない。

内部仕様が変更になった場合、外部に影響を与えることなく変更が可能だからである。
15 可変性を最小限にする 不変クラスはそのインスタンスを変更出来ないクラス。
インスタンスが生成された時点で、
クラスが保持する情報が決まり生存中変わらないことが保証されている。

オブジェクトの状態を保持するメソッドを持たない。
finalクラス。
全フィールドがfinal。
全てのフィールドはprivate。

不変クラスに出来ない場合も書く要素の可能な限り制限すべき
16 継承よりコンポジションを選ぶ 継承はカプセル化を破り、基底クラスに依存する。
is-a関係の場合のみ継承を利用する
17 継承のために設計及び文書化する、でなければ継承を禁止する 内部で利用するメソッドが継承を前提としている場合は、
実装の詳細をAPIドキュメントに記述する必要がある
18 抽象クラスよりインターフェースを選ぶ インターフェースはミックスインが可能。
ミックスイン=本来の型に加えて何らかの任意の振る舞いを提供する。
19 型を定義するためだけにインターフェースを使用する インターフェースとしての目的以外のためにインターフェースを実装するのは好ましくない。
定数はEnumや定数用のユーティリティで提供されるべき
20 タグ付クラスよりクラス階層を選ぶ 処理内容の種別を表すEnumを持つようなタグ付クラスは処理が冗長になり、
各処理の整合性を保つため難易度が高くなる。このようなケースは継承として実装すること。
21 戦略を表現するために関数オブジェクトを利用する 詳細は個別記事参照
22 非staticのメンバークラスよりstaticのメンバークラスを選ぶ 内部に定義するクラスには4種類(staticクラス、非staticクラス、無名クラス、ローカルクラス)がある。
メンバーのインスタンスがエンクロージングインスタンスへの参照が必要なら非static。
そうでなければstatic。
一箇所のみインスタンスを生成し、そのクラスを特徴付ける型が既にあるなら無名クラス。
そうでないならローカルクラスを利用する
5章 ジェネリック
項番 内容 詳細
23 新たなコードで原型を使用しない 原型=ジェネリックス指定を行わない形式。
原型はあくまで下位互換のための仕組みであるため
24 無検査警告を取り除く 無検査警告は可能な限り取り除く。
取り除くことが不可能だが、安全性の保証されているものは@SupressWarnings("uncheck")で警告を抑制する。
ただし、コメントを残すこと
25 配列よりリストを選ぶ 配列は共変。
配列のもととなるデータ型と同様に配列を親子クラスとして扱える。
配列は実行時に型エラーを検出するが、リストはコンパイル時に型エラーを検出する。
そのため、リストの方がより安全である
26 ジェネリック型を使用する クラスキャストを利用しているような場合、可能な限りジェネリック型の利用に置き換える
27 ジェネリックメソッドを使用する メソッドの戻り値をキャストを利用しているような場合、可能な限りジェネリック型の利用に置き換える
28 APIの柔軟性向上のために境界ワイルドカードを使用する あるクラスの継承クラスまで制限範囲を広げたい場合はextendsを利用して上限境界ワイルドカードを利用する。
あるクラスの基底クラスまで制限範囲を広げたい場合はsuperを利用して下限境界ワイルドカードを利用する。

利用基準はGetPutの原則に従う。
値の取得のみなら上限。
値の設定のみなら下限。
両方の場合はワイルドカードを利用しない
29 型安全な異種コンテナーを検討する クラスの型(Class)をMapのkeyに指定することでタイプセーフな異種データ保存コンテナを作成可能
6章 enumアノテーション
項番 内容 詳細
30 int定数の代わりにenumを使用する 詳細は個別記事参照
31 序数の代わりにインスタンスフィールドを使用する enumには序数(ordinal)が容易されているが、並び順に依存している。
EnumSetやEnumMapなどのための値であるためEnumに対応するintが必要な場合は別途フィールドを定義して、そちらを利用すること。
32 ビットフィールドの代わりにEnumSetを使用する ビットによる集合を取り扱うビットフィールドを利用する場合、EnumSetで置き換え可能。
33 序数インデックスの代わりにEnumMapを使用する 配列をインデックスするために序数を使用する必要性はほとんどない。EnumMapを使用すること。
34 拡張可能なenumをインターフェースで模倣する 詳細は個別記事参照
35 命名パターンよりアノテーションを選ぶ JUnit3以前とJUnit4を比較すると分かりやすい。
前者はメソッド名にtestとついているかどうかでテスト対象を判断するが、
後者はアノテーションの有無で判断する。後者のほうが厳密であり、パラメータも付加できることから拡張性があります。
36 常にOverrrideアノテーションを使用する オーバーライドするつもりが誤ってオーバーロードしてしまった、などという状況を防ぐために常にOverrrideアノテーションを使用すること
37 型を定義するためにマーカーインターフェースを使用する メソッド宣言を含まないマーカーインターフェースとマーカーアノテーションのどちらを利用すべきかは、型を定義したいかどうかによる
7章 メソッド
項番 内容 詳細
38 パラメータの正当性を検証する 詳細は個別記事参照
39 必要な場合には、防衛的にコピーする 詳細は個別記事参照
40 メソッドシグニチャを注意深く設計する メソッド名を注意深く選ぶ。
メソッドを提供し過ぎない。
多すぎる引数にしない。
引数の型はクラスよりインターフェースにする。
booleanの引数よりも2つの要素を持つEnumを使用する
41 オーバーロードを注意して使用する 利用者を困惑させるようなオーバーロードを使用しない。
同一数の引数を持つオーバーロードを行わない。
42 可変長引数を注意して使用する 引数チェックが煩雑になる、毎回配列を初期化するためパフォーマンスに問題がある。
など問題点があるために本当に必要な場合のみ使用すること。
43 nullではなく、空配列か空コレクションを返す 詳細は個別記事参照
44 すべての公開API要素に対してドキュメントコメントを書く 詳細は個別記事参照
8章 プログラミング一般
項番 内容 詳細
45 ローカル変数のスコープを最小限にする ローカル変数のスコープを最小限にすることで、
可読性の向上やバグの混入率を下げることが出来る。通常、宣言と同時に初期化を行う。
特別な理由がある場合のみ、宣言を分けるがその場合でも利用する直前で宣言すること。
46 通常のforループよりfor-eachループを選ぶ for-eachはforループと比較して、簡潔でありパフォーマンス上の欠点もないため利用可能な場合は必ずfor-eachを利用すること。
47 ライブラリーを知り、ライブラリーを使う 品質、パフォーマンス、工数など様々な面から既に存在するライブラリがあれば利用すべきである。
48 正確な答えが必要ならば、floatとdoubleを避ける floatとdoubleには小数点以下の誤差が発生するため、正確な結果が必要な場合は利用しないこと。
代わりにBigDecimalかint・longを利用すること。前者はパフォーマンスと使い勝手がネック。
後者は整数化して計算すること。
49 ボクシングされた基本データより基本データ型を選ぶ 選択可能な場面では基本データを利用する。パフォーマンスや意図せぬアンボクシングを防ぎます。
50 他の方が適切な場所では、文字列を避ける 標準入力から取得した数値や論理値などは文字列のまま利用せず、任意の型に変換して利用すべきである。
51 文字列結合のパフォーマンスに用心する Stringは不変であるため+演算子による結合が繰り返されるとパフォーマンスが劣化します。
繰り返し文字列結合をする際にはStringBuilderを利用すること。
52 インターフェースでオブジェクトを参照する 引数、戻り値、変数、フィールド等は適切なインターフェースがある場合、必ずインターフェースで宣言すること。
クラスで宣言する場合よりも変更に対して柔軟になります。
53 リフレクションよりインターフェースを選ぶ リフレクションには以下の欠点がある。
コンパイル時の型検査の恩恵がなくなる。
・読みにくく冗長なコードになるため可読性が下がる。
・パフォーマンスが劣化する。
一般に、実行時に普通のアプリケーション内でリフレクションによってオブジェクトにアクセスすべきではない。
コンパイル時点で分かっている型がある場合はインターフェースやスーパークラスを利用すること。
54 ネイティブメソッドを注意して使用する パフォーマンス改善を理由にネイティブメソッドを利用しないこと。
Javaの高速化が進んだこと、ネイティブメソッドの利用自体のオーバーヘッドなどから大きな効果は期待できないことが多い。
プラットフォーム固有の処理が必要である、など特別な理由がある場合のみネイティブメソッドを利用する。
55 注意して最適化する まずは良いプログラムを書くことに専念すること。
パフォーマンスの最適化は問題が発生してから対応すること。
実際に最適化を行う場合、
・プロファイラで原因を特定する
アルゴリズムを調査する。
改善しなければ低レベルな領域を最適化する
56 一般的に受け入れられている命名規約を守る 詳細は個別記事参照
9章 例外
項番 内容 詳細
57 例外的状態にだけ例外を使用する ループの制御や型チェックなど、例外以外の処理を行うために例外を利用しないこと
58 回復可能な状態にはチェックされる例外を、プログラミングエラーには実行時例外を使用する チェックされる例外(Exception)は回復可能な状態に使用。
実行時エラー(Runtime Exception)はプログラミングエラーに使用する。
59 チェックされる例外を不必要に使用するのを避ける チェックされる例外は呼び出し元のコードを複雑にするため必要最低限にすること
60 標準例外を使用する 詳細は個別記事参照
61 抽象概念に適した例外をスローする 処理の概念に合わせて例外を補足して投げ直すことを例外翻訳という。
例えば顧客情報取得処理でSQLExceptionを投げずにCustomerNotFoundExceptionを投げるようにする、など。
62 メソッドがスローするすべての例外を文書化する 各例外は必ずJavaDocの@throwsに記載すること。
63 詳細メッセージにエラー記録情報を含める 詳細は個別記事参照
64 エラーアトミック性に努める エラー発生時はエラーからの回復のため、呼び出し前の状態に戻すべきです。
最も簡単な方法は不変な設計をすることです。
次に好ましいのは、状態を変える処理の前にチェックを行うことです。
エラー前の状態に戻す処理を追加する方法もありますがあまり推奨されません。
オブジェクトのコピーに対して処理を行い、正常に終了した場合のみコピーの値を目的のオブジェクトに反映する方法もあります。
65 例外を無視しない 基本的には例外を無視しないべきだが、絶対に例外が発生しないが
文法上catchせざるを得ない場合はcatchブロックにコメントを記述すること
10章 並行性
項番 内容 詳細
66 共有された可変データへのアクセスを同期する
67 過剰な同期は避ける
68 スレッドよりエグゼキューターとタスクを選ぶ
69 waitとnotifyよりコンカレンシーユーティリティを選ぶ
70 スレッド安全性を文書化する
71 遅延初期化を注意して使用する
72 スレッドスケジューラに依存しない
73 スレッドグループを避ける
11章 シリアライズ
項番 内容 詳細
74 Serializableを注意して実装する
75 カスタムシリアライズ形式の使用を検討する
76 防御的にreadObjectを書く
77 インスタンス制御に対してはreadResolveよりもenum型を選ぶ
78 シリアライズされたインスタンスの代わりに。シリアライズ・プロキシを検討する

参考書籍

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)