基本的に「例外を握りつぶす」のは禁じ手に近いですが、まれに必要となる場面があります。
「例外を握りつぶす」≠「例外に何もしない」
まず、「例外を握りつぶす」というのはどういうことなのか、例として書いておきます。
void someMethod()
{
try
{
//何か例外が発生する処理
}
catch(SomeException e){}
}
このように、「例外をcatch
するけどその中では何もしない」ことを指して「握りつぶす」と呼びます。これは、そもそもtry
-catch
を書かないのとは全く違う行為です。
そもそも例外をcatch
しなければ例外は呼び出し元に渡り、さらに処理できるところへと向かっていきます。上位層で処理できればもちろんそれで一件落着ですし、最終的に何もなければ処理系側で異常終了の処理となります。一方、握りつぶした場合は例外が闇に葬られ、何事もなかったことにして処理は続行してしまいます。
必要がなければ例外処理を書かずに上位層へ任せればいいだけです(特に、本当に「例外的」であって対応不能なもの、あるいはプログラムミスでなければ起き得ないものはどこでもキャッチせずに異常終了あるいはデバッガに渡したほうがいいでしょう)。
握りつぶす「意味」
それでは例外を握りつぶすと何が起きるのでしょうか。
- 例外が発生したことを、ユーザー・プログラマ含め、誰も知ることができません1。
- プログラムはそのまま実行され続けます。
つまり、握りつぶしてもいい例外というのは、「例外が起きたとしても、誰にも知らせる必要もないし、何もせずにそのままプログラムを流していい」、そんな例外に限られます。RubyやPythonには、StopIteration
という、イテレータが終了したことを知らせる例外がありますが2、こういうのはむしろ「握りつぶす」ためにある3、異質な(例外的状況を示すのではない)例外です。
そういう状況以外で握りつぶして問題ない例外は、「環境によって呼べる呼べないが変わるけど、呼べなくても別に誰かへ知らせるほどでないメソッドの呼び出しに失敗した場合」とか「入力ファイルのクローズに失敗した場合」、「ファイナライザの後片付けに失敗した場合」4など、ごく限られたものです。
Java特有の事情…検査例外
ここで1つややこしいのが、Javaには「検査例外」と呼ばれる種類の例外があって、これらはメソッドが投げる場合にはその旨を宣言しなければならないということです。つまり、検査例外を投げるメソッドを呼び出した場合、「呼んだメソッド自身が同じ例外を投げると宣言する」あるいは「内部でcatch
してその例外は外に投げない」の2つの選択肢しかなく、正しく対処しなければコンパイルが通りません。
ただし、起きると宣言されていても「これは絶対に起きない」と確信を持って言える場面もあるかと思います。たとえば、あるクラスについて、どうしても外部から、ドキュメント化もされているprotected
メソッドを呼ばないといけなくなったとします。ここでメソッドを取得するためにgetDeclaredMethod()
を呼べば、検査例外としてNoSuchMethodException
が宣言されています。しかし、ドキュメント化もされているメソッドである以上、これに失敗するケースというのは考えられない…となって、例外を握りつぶしたくなるかもしれません。
ここでさらに考えを進めてみると、この呼び出しでNoSuchMethodException
となるケースは、「呼ぶメソッド名やシグネチャを間違えていた」という凡ミスを別とすれば、「互換性が崩れるほどの改変がクラスライブラリに入った」「間違ったライブラリをロードしてしまった、あるいは攻撃者が不適切なライブラリに差し替えた」など、どう考えてもプログラムの実行を続けるべきでない、かなり致命的な事態であることがわかります。このような場合はちょうどいいエラーとしてNoSuchMethodError
がありますので、それを投げましょう。**Error
**は検査例外ではない(そもそも例外ですらない)のでどこからでも投げられますし、一般に例外処理の過程でError
はキャッチしませんので、ちゃんとそこでプログラムを終わらせてしまうことができます。