アプリケーションを作成していると、メインとなる機能をコーディングしているときが一番の醍醐味になりますよね。実現したい機能をどんどん組み上げて、動作の確認がとれたときには嬉しさが込み上げてきます。ただプログラムの動作にはエラーが付き物です。そういった想定されるエラーや期待していない動作への対処方法について今回はまとめてみたいと思います。
プログラム上のエラーとは
エラーと言っても文脈によってニュアンスが異なることがありますが、ここではプログラム上の「期待していない動作」と定義することにします。配列の指定したindexが範囲外、readする対象のファイルが存在しない、NULLポインタなど、プログラムを書いていれば必ずと言っていいほどエラーに遭遇することになります。特に言語に慣れていないうちはエラーを頻発させてしまうこともありますし、ある程度以上の規模の開発になればそのぶん、エラーの発生率は高まることでしょう。
コーディング作業の半分以上はエラーへの対処、つまりデバッグに割かれるという話もあるくらい、エラーやバグに向き合うことはソフトウェア開発において重要な事項となっています。では未知のエラーなどに対して具体的にどのように向き合っていけばよいのでしょうか?以下で順を追って説明したいと思います。
例外
プログラムの教科書で必ず出てくる例外という言葉。一見わかりづらいのですが、簡単に言うと「エラーが発生したことをプログラム上で通知する仕組み」になります。ライブラリやメソッドの中で何かよろしくない事態が発生するとこの例外を送出し、プログラム上で異常を通知します。例外には様々な種類がありますが、大きく分けて3つに分類されます。
- 検査例外(Exception)
- 非検査例外(Runtime Exception)
- エラー(Error)
検査例外は主に想定されるエラーを通知するもので、発生場所で捕捉(catch)して何らかの処理を行なうか、呼び出し元に例外を投げて(throw)制御を渡す必要があります。
非検査例外は主に想定されないエラーを通知するものなので、捕捉は必須ではありません。
エラーは例外というよりはシステム上の動作に影響する問題のことで、これも捕捉は必要ありません。
例外と聞くとまず思い浮かべるのはtry-catch文かもしれません。検査例外ではこれか、throws節を使って呼び出し元に例外を投げる必要があります。Javaの場合のそれぞれの書式はこのようになっています。
try {
//例外が発生する可能性のある処理
} catch (IOException) {
//例外が発生した場合の処理
} finally {
//最後に必ず実行する処理
}
public void sampleMethod() throws IOException {
//メソッド処理
}
例外を投げることでエラーが発生していることを確実に通知できるというメリットがあります。その一方で、捕捉した方はきちんと処理を書かねばならずコードの複雑さは増えることになります。例外が発生する場合にはどこで処理をするのか、捕捉した側はどこまでの対処をしないといけないか、などをよく見極めてエラーをコントロールできるようになりましょう。
エラーへの対処方法
検査例外では捕捉した後どのように処理をすればよいでしょうか。また非検査例外やエラーが発見された際にはどのように対処すべきでしょうか。一般的な方法について見ていきましょう。
ログを出力する
本質的な対処以前の話ではありますが、まずはエラー内容をログに出力しておきましょう。エラー原因を探る際にログは大いに参考になります。このときできるだけ詳細に手がかりを記録しておきたいので、例外の場合にはスタックトレースも出力しておきましょう。エラーの発生箇所や呼び出しの経緯などがわかりやすくなります。
処理の継続 or 中止
例外を捕捉したりエラーを検出したとしても、全体の処理において大きな問題が発生しないことがわかっていれば、処理を継続させることもできます。逆に必要な処理が実行されず、以降の処理に重大な影響がある場合には処理やアプリケーションを中止する必要が出てきます。エラーを一律に処理するのではなく、まず対象のエラーが全体の処理に与える影響について理解した上で適切な処理フローを実行できるように設計しましょう。
デフォルト値を使用する
不正なデータに対して処理を継続したい場合、とりあえずデフォルト値(intであれば0など)を使用するという選択肢もあります。ただしデフォルト値の影響やエラー頻度などを考慮して慎重に決める必要があります。値としては初期値、平均値、最頻値などが候補になります。
エラー処理の共通メソッドを呼び出す
「ログに内容を出力する」「アプリケーションを終了する」などの一連の処理をまとめた共通メソッドを作成しておき、エラーを検知した際には必ずそのメソッドを呼び出す方法もあります。一見便利なように見えますが、何も考えず呼び出すようになってしまうと個々のエラーへの配慮がおろそかになるので、必ずしもベストな方法ではないことを意識しておきましょう。
エラーメッセージを表示する
特にGUIアプリケーションなどの場合には、メッセージを表示してエラーを通知する必要も出てくるかと思います。ただし必要以上の詳細な情報まで表示してしまうと悪意のあるユーザに利用されてしまう可能性があるので注意が必要です。

参考文献
例外やエラー処理についてはこちらの2冊を参考にさせていただいています。
「CODE COMPLETE」は用語など若干古さを感じる部分がありますが、エラーや例外の考え方や対処についての原則は現在でも適用できることばかりです。非常に詳細に書いてあるのでどれだけ実践できるかあまり自信がありませんが、理想的な思想については深く勉強になることが多いです。
「Java本格入門」は基礎情報はある程度理解している前提で、その知識を実践に落とし込む際のTipsや注意点がまとまっている良書です。コーディングをしていて普段から少し気になっていた点がピンポイントで項目として取り上げられていたりして、より実用的な場面で役に立つ情報が多く載っています。
まとめ
今回は例外の仕組みやエラーへの対処方法について取り上げました。本来作りたいアプリケーションの機能部分からは離れているように見えますが、エラーへの対処やデバッグを通してアプリケーションの要件が明確になったり、開発者の理解が深まったりします。エラーには必ず原因が存在します。エラーを想定したコードを書いたり、エラーが発生した場合には謎解きのつもりで原因究明を行なうなど、エラーを通じてエンジニアとしてのレベルアップに励んでみてください。
最後までお読みいただきありがとうございました!