LoginSignup
72
65

More than 5 years have passed since last update.

例外を握りつぶす

Posted at

基本的に「例外を握りつぶす」のは禁じ手に近いですが、まれに必要となる場面があります。

「例外を握りつぶす」≠「例外に何もしない」

まず、「例外を握りつぶす」というのはどういうことなのか、例として書いておきます。

握りつぶし
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はキャッチしませんので、ちゃんとそこでプログラムを終わらせてしまうことができます。


  1. もっとも、変数値などで伝わる、ということはありえます。 

  2. RubyのenumeratorではhasNextのようなメソッドがなく、(数を数えるという原始的な方法を除けば)この例外でしか終了を知ることができません。 

  3. Rubyでは、loop{}のブロック内でStopIterationが起きると、その例外はloopが握りつぶしてしまって、平然とループを終了します。 

  4. Javaの場合は、何もしなくてもファイナライザが投げた例外は闇へと消えていってしまいます。 

72
65
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
72
65