ぬぅ、1ヶ月以上空いてしまった。メモがもったいないのでこっちの方にも書いておこう。
まぁ、最近はJavaなんて全然触れていないのですが!(´Д`
--------------
39.通常フローに例外を使うなんてトリッキーなことはしない
上手く設計されたAPIは利用者側に通常フローで例外を使わせない。
例:Itratorのnext()→状態依存だが... 状態を検査するhasNext()がある!
hasNext()が上手く使えない場合→オブジェクトが外部同期無しで並列にアクセスされる場合
hasNextとnextを一つにまとめ、返り値で判断できるようにする(nullや-1とか)方法もあるが...できれば状態検査メソッドがあったほうがコードは読みやすい。
--------------
40.例外の種類
例外: -チェックされる(catchされるべき)例外
-実行時例外(Runtime Exception系)
-エラー
チェックされる例外以外は一般に回復不能なもの。エラーを隠蔽してしまうので、例外処理はしない方がいい。
だからRuntime Exception系のものは発生する可能性があっても特にtry文は求められない。
(けど個人的にjava.lang.IllegalArgumentException派生のモノは例外処理アリなんじゃないかなぁとも思ってる。よく数値っぽい文字列かどうか判定するのにNumberFormatExceptionを使ってるんだけど、これは卑怯でしょうか?)
-例外が発生しても対処可能ならチェックされる例外を使う。(外部リソースに依存している場合とか。DB、filesystem等)
-利用者のプログラミングエラーを示すなら実行時例外。
例外も一つのオブジェクト。自分で例外クラスを作るならエラー情報のアクセサを作るのもアリ。
--------------
41.チェックされる例外を多用しない
例えば、自作ライブラリーがチェックされる例外を投げて、利用者がcatchしてもe.printStackTrace()ぐらいしかやる事がなければ実行時例外の方を使うべき。
チェックされる例外の発生はライブラリー利用者に負荷をかける。
利用するのにtry~catch文が必要になる。
↓負荷を減らすには...
失敗か成功かを表す返り値booleanなラッパーメソッドを作る(でも、39のhasNext()同様にスレッドセーフであることが求められる。ラッパーメソッド実行中にオブジェクトの中身が変化して間違った値が返ってきたら意味がない。)
--------------
42.用意されている例外を使え
新しい例外を作ると覚えることが増えてメンドイ。
よく使われる例外
・IllegalArgumentException
・IllegalStateException
・NullPointerException
・IndexOutOfBoundsException
・ConcurrentModificationException
・UnsupportedOperationException
-NullPointerExceptionとIndexOutOfBoundsException
渡ってきたパラメータの検査時に使う。これらはIllegalArgumentExceptionより具体的に例外内容を示している。
-UnsupportedOperationException
→コレを投げるようならそもそもそんなメソッド作るなという話だが、インタフェースを実装するときに使える。
本では例としてremove不能なListを実装する場合を挙げている。
--------------
43.例外翻訳(Exception translation)
AbstractSequentialList#get(int index)より。 public E get(int index) { ListIterator e = listIterator(index); try { return(e.next()); } catch(NoSuchElementException exc) { throw(new IndexOutOfBoundsException("Index: "+index)); } }
この場合、要素が見つからないというのは渡されたindex値がリストの範囲を超えてる場合なので、そんな要素ありませんという例外よりはリストの範囲外だぞという例外を投げた方が利用者にとって分かり易い。
このように下位レイヤーが投げる例外と上位レイヤーで例外の意味合いが代わってくるときに.例外翻訳イデオムを使用する。
デバッグ用に下位レイヤーで発生した例外を新たに生成する例外のコンストラクタに渡すことで例外情報を保持し続けることもできる。(例外連鎖)
#もっとも、一番良いのは下位レベルで発生した例外を上位レイヤーに投げずに済むように中で例外を片づけることだけどね。
--------------
44.例外を投げるメソッドのドキュメント
jacvadocコメント内で@throws タグを使ってメソッドが投げる例外について記述しようという話。
→例外を記述することにより、そのメソッドを実行するための事前条件が記述される。
クラス内にあるほとんどのメソッドの事前条件がnot nullな場合毎回書くのか?
→クラスドキュメントにまとめて書くのもok。
--------------
45.例外の文字列表現
例外クラスを自作する際のtoString作事の注意点について
→例外を発生時のパラメータとフィールド値は全て記録しておくことが望ましい。
例:IndexOutOfBoundsException
範囲の下限上限と実際に例外を発生させるキッカケになったパラメータ値を保持してるべき。
→詳細情報の提供を強制させる為、例外オブジェクト生成時にこれら上限下限とパラメータ値を引数として要求するコンストラクタを作るのもアリ。
--------------
46.エラーアトミック性
例外が発生したら、オブジェクトの状態は例外が発生する前の状態に戻っていることが望ましいという話。
一番ラクなのは最初に引数チェックをしてオブジェクトの状態を変化させる前にエラーを検知すること。
それが無理なら状態戻すコードを追加すること(DBで言うところのroollbackに相当)
一時的なコピーをつくってそれに対して操作するという方法もある。
エラーアトミック性を達成できないモノもある。
(ConcurrentModificationExceptionを投げたオブジェクトに関しては中身が壊れてる状態なので元に戻すことは出来ないだろう。)
--------------
47.例外は無視しない
catchブロックが空なコードは書かない。まぁ、当たり前か。
仮にしょうがなかったとしても、なんで空なのか説明するコメントが必要。