例外をどう書くか
Webアプリのメンテナンスをしていて,例外の扱いがいい加減なのが気になり(元は他人のコード),例外周りを整理しようと思い立った。さてJavaの例外はどう扱うのがスマートか。色々探してみて,役に立つと思ったのが以下の3つ。
例外(Exception)脳の作り方
- 戻り値にエラーコードをセットするぐらいなら例外を投げよう
- そうすれば正常系だけがコードの前面に出るヨ
めっちゃ分かりやすいっす。例外のうまい使い方が納得できるなあ。ここでは自分で分かりやすい例外クラスを作って活用する方針。
Best Practices for Exception Handling
- checked exceptionは使うな(情報の隠蔽が壊れる)
- 自分で例外クラスはあんまり定義するな(標準の例外クラスを使うかRuntimeException("custom error")を使え)
という方針で,例外をuncheked exceptionにして投げ直す方法を推奨している。
Exception Handling in Web Applications
ここでもchecked exceptionは悪者。NestedExceptionというクラスを作り,例外を補足したらNestedExceptionにラップして(ここ重要。元の例外オブジェクトがそっくり残るのが利点)投げ直している。NestedExceptionは全ての例外をラップするRuntimeExceptionのサブクラス。これをweb.xmlのerror-pageでさらに補足して,意味のあるエラーメッセージを画面に表示するという仕組み。ここで紹介されている3つのアンチパターンがおもしろい。
- アンチパターン:多すぎる情報
例外をログした上に投げ直す catch (Exception e) { e.printStackTrace(); throw new WrappingException(e); }
- アンチパターン:省略の嘘
ログした例外と投げた例外が異なる catch (Exception e) { // print message only. System.err.println(e); throw new WrapperException(e.getMessage()); }
- アンチパターン:
砂の中の頭(?)現実逃避
例外を無視する catch (Exception e) { e.printStackTrace(); }
checked exception(try/catchするか,クラスがthrowしないといけない例外)は,情報の隠蔽という面からして望ましくない。例えばユーザのIDから属性を取得するクラスがあるとして,このクラスがSQLExceptionを投げていると,クラスを使う側ではこの例外をハンドルしないといけない。ところがSQLを使っているかどうかは,元クラスの実装に依存しており,これをcatchするとクラスを使う側までそれに依存してしまう。
unchecked exceptionはハンドルする必要がない(強制されない)ので,こういう問題が少ない。個人的に方針をたてるとすると,
- 独自例外は必要に応じて作る
- できるだけunchecked exceptionを使う
- 下位レベルからの例外は分かりやすい例外で投げ直す
って感じでしょうか。
しかし独自例外を作るときは例外1つにつき1ファイルですよね。調子に乗ると例外を記述したファイルばかり増えちゃうな。