2011-12-06
multi-catchを信じていいのか?
Java |
みなさんお元気ですか?
もうじきクリスマスですが、今年の僕にはクリスマスがあります。
まあ、そんなのろけ話はどうでもいいとして、
Java7から新機能として実装されたmulti-catchですよ。multi-catch。
例外処理のリファクタリングをしていてこんな疑問が沸きました。
multi-catchって必要なんかな。検査例外はキャッチした内容によってif文のように書き分けるもんじゃないのか?それともproject lamdaとかで良い感じに使うためにあるのかな?
非検査例外は復帰できないパターンが多いのでプログラム落とすことが多いけど、検査例外は復帰できるパターンもあるから捕まえた例外によって処理書き分けない?
この辺りですごく混乱してるのが見て取れます。
と、思って今Effectiv javaパラパラめくって調べて気が付いた。非検査例外と検査例外逆に考えてた。検査例外が復帰できなくて、非検査例外が復帰できるんだ。
@Crimson_Apple え、元ので正しいと思いますが・・・
2011-12-05 15:30:56 via Tween to @Crimson_Apple
@bleis うぇ?ちょっと混乱してますね、自分。整理してみます。
2011-12-05 15:33:05 via tGadget to @bleis
@bleisさんの指摘がなかったら本当にそのまま思い込んでいるところでした。
間違いを正していただき本当にありがとうございます。
まとめる意味もこめて、各所で散々議論され尽くされ、もう精も根も尽き果てている例外さんですがもう一回復習してみます。
検査例外ってなぁに?
- Exceptionをextendsしている例外クラス。
Exception クラスとそのサブクラスは、通常のアプリケーションでキャッチされる可能性のある状態を示す Throwable の形式の 1 つです。
- この例外をthrowするメソッドを呼び出す側はtry-catchするかthrows句で指定しなければならない。漏れていたらコンパイルエラーとなる。
- 例外処理を行っていることをコンパイラが「検査する」。だから検査例外。
- try-catchもしくはthrows句を強制するため、必ず例外処理を書かなければならない。この性質から検査例外は復帰可能な例外と考える。
非検査例外ってなぁに?
- RuntimeExceptionをextendsしている例外クラス。
メソッドの実行中にスローされるがキャッチされない RuntimeException のサブクラスについては、メソッドの throws 節でそれらを宣言する必要はありません。
- この例外をthrowするメソッド呼び出しても呼び出す側はtry-catchをしなくてもよい。throws句を記述する必要はない。
- RuntimeException自体はExceptionのサブクラスであるが、コンパイラは例外的に「検査しない」。だから非検査例外。
- 非検査例外は例外処理を強制されない為、実行するまで何が起きるか分からない。この性質から基本的には復帰できない例外と考える。
- 故に、実行時例外なんていう言い方もする。
大体こんな感じ。至極簡単にまとめると
multi-catchってなぁに?
で、こっからが本番です。
multi-catchとはProject Coinで策定されたJava7からの新機能です。
Java1.6以前はこんな書き方でした。
try { Document doc =DocumentBuilderFactory.newInstance() .newDocumentBuilder() .parse(new FileInputSream(filename)); (省略) } catch (IOException e) { (ログの出力や復帰処理など。) } catch (IllegalArgumentException e) { (ログの出力や復帰処理など。) } catch (ParserConfigurationException e) { (ログの出力や復帰処理など。) } catch (SAXException e) { (ログの出力や復帰処理など。) }
非常にもっさり。
ソースに漂うもっさり感を取り去る為Java7からこんな書き方が出来るようになりました。
System.out.println("hoge"); try { Document doc =DocumentBuilderFactory.newInstance() .newDocumentBuilder() .parse(new FileInputSream(filename)); (省略) } catch (IOException | IllegalArgumentException | ParserConfigurationException | SAXException e) { (ログの出力や復帰処理など。) } System.out.println("fuga");
かなりすっきりしました。
でも、ちょっと待って欲しい。
try-catchされる例外は基本的に復帰可能な例外です。
つまり、例外が起きたとしてもちゃんと処理するならばプログラムの実行を許される契約です。
try {
Document doc
=DocumentBuilderFactory.newInstance()
.newDocumentBuilder()
.parse(new FileInputSream(filename));
(省略)
} catch (IOException e) {
(ログの出力や復帰処理など。)
} catch (IllegalArgumentException e) {
(ログの出力や復帰処理など。)
} catch (ParserConfigurationException e) {
(ログの出力や復帰処理など。)
} catch (SAXException e) {
(ログの出力や復帰処理など。)
}
System.out.println("fuga");
以上の例ではhogeが出力された後、ちゃんとfugaが表示されることが期待できます。
fugaが表示されない場合は、catch句throwし呼び出し元で例外処理されることを期待するときや、例外が起きることを想定してcatch句内部でreturnしたときです。
つまり、catch句はif文のように振舞うわけでそれぞれの例外に対応した処理を書かないといけないんじゃないの?と思うわけです。
multi-catchを利用することによって様々な例外を一括処理することが許されるようになりましたが、様々な例外が一括して処理されるような状況ってよもやthrowするかログの出力くらいしかありません。
散々忌み嫌われてきたコレにも似た状況に陥るんじゃないかと思ってしまうくらいです。
try { (省略) } catch (Exception e) { e.printStackTrace(); }
まとめ
正直なところ、multi-catch使うときはちゃんと例外の契約に沿っているのかを熟考して使わないいけないという感想を抱きました。
そうでないと復帰させれるはずの例外までmulti-catchで丸めてしまい、バグの温床*1になるんじゃないかとおっかなびっくり。
///)
/,.=゙''"/
/ i f ,.r='"-‐'つ____ こまけぇこたぁいいんだよ!!
/ / _,.-‐'~/⌒ ⌒\ 糖衣構文なんだから!!
/ ,i ,二ニ⊃( ●). (●)\
/ ノ il゙フ::::::⌒(__人__)⌒::::: \
,イ「ト、 ,!,!| |r┬-| |
/ iトヾヽ_/ィ"\ `ー'´ /
では許されないような気がします。
参考元
Effective Java 第2版 (The Java Series)
Throwableについて本気出して考えてみた
Throwableについて本気出して考えてみた 2nd Season
Java SE 7徹底理解 第1回 言語仕様の小さな変更 - Project Coin
- 40 http://d.hatena.ne.jp/amatanoyo/20101019/1287414497
- 24 http://t.co/rBZbwKsk
- 12 http://b.hatena.ne.jp/entry/b0r0nji.blogspot.com/2011/12/blog-post.html
- 6 http://htn.to/x9rkaR
- 6 http://longurl.org
- 5 http://d.hatena.ne.jp/JunichiIto/20111102/1320253815
- 5 http://t.co/rdsqBoC5
- 4 http://ezsch.ezweb.ne.jp/search/?sr=0101&query=学歴 高い人
- 2 http://b.hatena.ne.jp/t/java
- 2 http://d.hatena.ne.jp/amatanoyo/mobile?date=20101019