読者です 読者をやめる 読者になる 読者になる

「例外」というもの

設計 Java

 プログラミング言語には「例外」というものがたいがい、備わっているわけですが、それについていろいろ思うところがあるのでまとめておこうと思います。

Java の例外は何が素晴らしいか

 まず Java の例外のメリットを並べておきます。

a. プログラムのメイン処理の見通しが良くなります。
 継続不可能なエラーが発生したときに処理本体から"脱出"させることで、メイン処理の記述にエラー処理が紛れ込む、ということを抑止出来ます。

b. エラー内容を適切に呼び出し側に伝えられます。
 エラーが発生したとき、どういうエラーが発生したのか、というのはエラー対処方法を決定する根拠になります。「どういうエラーなのか」ってことを例外オブジェクトに意味付けしておくことで、呼び出し側に責任転嫁出来ます。ライブラリ設計者がエラーにどう対処するか、ということまで責任持たなくても良くなるわけです。

c. エラー対処を適切な場所で行えます。
 エラーが発生したとき、そのエラーに対応できるかどうか、というのはクラスの粒度に依存します。従って、エラーの種類によっては呼び出し側が対処できないこともあるわけです。そういう場合に、例外を使えば無理に責任を取る必要はなく、さらに上位の呼び出し側に対応をお願いすることができます。

d. 発生し得るエラー群に対応漏れが無いかコンパイラがチェックしてくれます。
 いわゆる検査例外というやつです。"こんな例外が発生するよ" と明示しておく代わりに、その例外への対処を記述しないとコンパイルが通らないようになっています。これは、いちいちプログラマが発生し得るエラー一覧を確認して網羅しないといけない、というヒューマンエラー入りまくりのリスクを回避してくれます。

 さしあたりこんなところでしょうか。

 ここでまずポイントになるのは b。エラー内容を呼び出し側に伝える方法として、例外を使わない方法も十分にあり得ます。例えばエラーの種類を戻り値として返したり、エラー受け取り用のオブジェクトを渡したり。もしくはグローバルオブジェクトにエラーを格納するようにしておいて、呼び出し側でそれを確認してもらったりすることも考えられます。
 しかし、そうした方法はそもそもエラー処理のための機構ではないので、どういう手法を使っているのかを利用者が確認しないと痛い目を見ます。また、ありがちな例としてエラーの種類を enum にしておくという手がありますが、そうすると複数の処理でその enum を共有しがちになり、その結果処理によっては発生するはずのない列挙子というものが出てきます。すると、処理ごとに発生し得る列挙子と発生し得ない列挙子をプログラマがいちいちチェックすることになり、結局ヒューマンエラー入りまくり、という事態になります。

 で、ここまで読んでいただければとりあえず分かるかと思いますが、例外の素晴らしいところは、混沌としがちな「エラー対処」をサポートするために上記4つのメリットを"セットで提供"してくれていることです。この機構があるおかげで、エラー処理は例外機構へ集約されました。「俺々エラー機構」からの脱却が成されたわけです。

Java の例外の何がまずいのか

 さてさて。
 Java の例外の大事なトコロをかじったところで、デメリットについて説明しようかと思います。ある機構を使いこなすには、その機構のデメリットを知ることが近道ですしね。

A. 検査例外である故に、エラー対処が非常に面倒になる。
 処理の都合上"どうやったって発生し得ない例外"であっても、catch 節を用意しないとコンパイルが通りません。また、エラー処理は後回しでとりあえずプログラムを動かしたい、という場合にもコンパイル通らないことが足かせになります。

B. オブジェクト指向プログラミングの「継承」と相性が悪い。
 オブジェクト指向を謳った Java という言語で、オブジェクト指向プログラミングと相性が悪いなんて本末転倒もいいとこです。これは検査例外とも絡むのですが、基底クラスの例外仕様を外れた例外仕様は定義できません。

 大きく言うとこの2つでしょうか。
 A の弊害は非常に大きいです。コンパイルエラーが欝陶しいので必殺「握りつぶし」が発動します。何でも catch( Exception e ) にしてしまうということですね。また、Java には RuntimeException というクラスがあり、こちらは検査されない例外となっています。検査されないようにするために利用されちゃったりするわけですね。
 続いて B について補足説明します。
 例えば基底クラスのメソッド func が IOException 投げる場合、派生クラスの func では IOException を継承した例外以外の例外は投げられません。なぜなら、func の呼び出しを含むライブラリに派生クラスのインスタンスを渡したとき、想定外の例外を投げるとそれに対処する catch 節が記述されている保証が無いからです。派生クラスが「どんな実装をするか」は基底クラスが知る由もないので、派生クラスが「どのような例外を発生させるべき処理を行うか」もまた基底クラスの知るところではありません。しかしそれを許してくれない、という矛盾があります。

C++ の例外事情

 では、こうしたデメリットを克服するような例外機構はないものでしょうか。Java 以外のプログラミング言語の例として、C++ の事情を紹介します。(まぁその、異なる言語間で比べちゃだめだろうという突っ込みは置いといて)
 C++ でも Java と同様の例外機構が備わっています。大きく違うのは非検査例外であるということです。検査されないので、メリット d がなくなってしまいます。しかしデメリット A, B は解消されます。例外を catch するかどうかは利用者の責任になるわけです。もし、実際に例外が throw されて catch されなかった場合、プログラムは終了します。まぁ、C++ では不正メモリアクセス発生させて簡単にプログラム終了させられるので、あまり驚くことでは無いです。
 しかし一方で C++ には型安全性を確保する言語仕様が厳密に規定されています。また、C++ には template を使ったメタプログラミングがあります。これは型安全性を保ったままでプログラムを増産する仕組みで、非常に高い生産性を確保することができる機構です。C++ では型というものについて厳密に安全性を確保してくれるわけです。しかし例外については甘甘な安全性しか確保できないというのはなんともバランスが悪いと思うのです。その結果、C++ のプログラミングスタイルはどうなるかというと、例外が発生し得る処理があったら、せめて利用者側の処理を破綻させないように、「例外が発生してもなんとかするコードを書くしくみ」が発達しました (デストラクタを finally のように使ってリソースの破棄を確実に行う、等のテクニックのことです)。

 これはこれで価値のあることなのですが、例外機構が本来解消すべき(だと僕が勝手に思っている)、「エラー対処の安全性向上」の決定打にはなっていません。

例外の責任は誰のものか

 ではここでいったんオブジェクト指向との絡みについて再考してみたいと思います。先の説明で、オブジェクト指向の「継承」と検査例外は相性が悪い、ということを述べました。
 では例外機構がオブジェクト指向と相性良くなるためには、どうあるべきでしょうか?
 オブジェクト指向では実装隠蔽が是とされます。そして、あらゆる場面において誰が責任を持つのか、ということを最大限考慮します。となると「派生クラス独自の処理で発生する例外にどんなものがあるのか」は派生クラスが一番知っているはずですから、派生クラスが責任を持つような仕組みになっているべきだろうと思うのです。これが一つ目のテーマです。この考え方は総称性プログラミングを使った静的多態においても同じことが言えます。

例外はどれくらい例外的か

 さて、今度は C++ の話に飛んで二つ目のテーマを述べます。いやぁ、あちこち飛び回って申し訳ない。
 ポイントは例外に対する考え方の違いです。C++ では Java ほど頻繁には例外を投げない傾向があります。「例外は本当に例外的な場合にのみ投げるべし」という考え方ですね。これはこれで正しいのですが、はてさて、どうなんでしょうか。
 「本当に例外的」というのが曲者なのです。二つ目のテーマは『「本当に例外的」かどうかは、設計粒度に依る』という点です。つまり、ライブラリ側が「これは本当に例外的だから throw する」「これはそれほど例外的ではないから throw しない」という判断をすることがそもそも間違っているのではないか、ということです。Java のやりかただと厳しすぎて、C++ のやりかただと甘すぎる、という実態の原因はここにあるのではないでしょうか。

まとめ

 ずいぶんと長くなってしまいました。本稿でも僕の中でも結論が出ていないのですが、最後にまとめておきます。

 まとめ:例外機構はこうあるべき(だと思う)
  
  1. メイン処理の見通しをよくするものであること
  2. 発生したエラーの内容を的確に表現できるものであること
  3. 発生したエラーへの対処を的確な場所で行えるようなものであること
  4. 発生し得るエラーにどんなバリエーションがあるかは、抽象部・共通部が関与しないこと
  5. 個々のエラーがどの程度の危険性を持つかは、その処理の利用者側が決められるようなものであること

 そんな例外機構をまだ見たことが無いのですが、もしご存知でしたら教えて欲しいです。非常に興味があります。
 まーそんなわけで、なーんかご意見いただけると幸いです。