例外処理(エラーハンドリング)
「処理が続けられない事態」をエラーとして投げ、呼び出し側がまとめて受け止める仕組み。本来の処理とエラー処理を分けて書けるのが利点。
- 1.例外処理は 異常事態を“投げて(throw)”、対処できる場所で“捕まえる(catch)”仕組み。正常系とエラー系を分けて書ける。
- 2.捕まえなければ呼び出し元へ自動で伝播し、最後まで誰も処理しなければプログラムが落ちる。finally は成功・失敗どちらでも必ず実行される。
- 3.最大のアンチパターンは“握りつぶし”(catch で何もしない)。リソース解放は finally / using で確実に、catch は本当に対処できる所だけに置く。
なぜ「戻り値」で返さないのか
エラーを「特別な戻り値」で表す方法(後述のエラーコード方式)には弱点があります。呼び出すたびに結果を確認しないと、エラーを見落とせてしまうこと、そしてエラー処理のコードが本筋に混ざって読みにくくなることです。
例外方式は、異常が起きた瞬間に処理を中断し、チェックを書き忘れても勝手に下へ進めないようにします。これにより「気づかないままおかしな状態で進む」事故を防げます。
// 例外を投げる側
function divide(a, b) {
if (b === 0) {
throw new Error('0 で割れません'); // 異常を“投げる”
}
return a / b;
}
// 受け止める側
try {
const result = divide(10, 0);
console.log(result); // ここは実行されない
} catch (err) {
console.error(err.message); // "0 で割れません"
}
try / catch / finally
例外処理の基本は3つのブロックです。役割を分けて覚えると混乱しません。
| ブロック | いつ実行されるか | 役割 |
|---|---|---|
| try | まず必ず入る | 失敗するかもしれない本来の処理を書く |
| catch | try の中で例外が起きたときだけ | エラーの後始末・代替処理・ログ |
| finally | 成功でも失敗でも必ず最後に | リソース解放など、どちらにせよ要る後片付け |
try の中で return しても、finally はその後で必ず実行されます。途中で抜けても確実に動くので、ファイルや接続を閉じる処理の置き場所として最適です。逆に finally の中で return すると try の戻り値を上書きしてしまうので、そこに return は書かないのが無難。
エラーの伝播(どこで捕まえるか)
catch がない関数で例外が起きると、その例外は呼び出し元へ自動的にさかのぼっていきます。これを伝播(propagation)と呼びます。途中の関数が捕まえなければ、さらに上へ。最後まで誰も catch しなければ、プログラム自体が異常終了します。
function load() {
throw new Error('読み込み失敗');
}
function process() {
load(); // ここでは catch しない → そのまま上へ伝播
}
function main() {
try {
process(); // ここでまとめて受け止める
} catch (err) {
console.error('処理を中断:', err.message);
}
}
ポイントは、**「対処できる場所まで素通りさせてよい」**ということ。途中の関数が中身を知らなくても、適切な場所で1か所だけ捕まえれば済みます。これが「正常系とエラー系を分けられる」という利点の実体です。
低レイヤーの関数で何でもかんでも捕まえると、上の層は「成功したのか失敗したのか」が分からなくなります。自分が本当に対処(リトライ・代替・ユーザー通知)できる層でだけ捕まえ、それ以外は伝播に任せるのが基本。捕まえたけど対処できないなら、throw で投げ直しましょう。
エラーコード方式 vs 例外方式
エラーの伝え方には大きく2系統あります。言語によって主流が異なり(Go や C はコード方式寄り、Java や Python は例外寄り)、どちらが「正しい」というより向き不向きがあります。
| 観点 | エラーコード方式(戻り値で返す) | 例外方式(throw / catch) |
|---|---|---|
| 伝え方 | 戻り値やエラー値で表す | 例外を投げて中断・伝播させる |
| 見落とし | チェックを書き忘れると素通り | 捕まえないと勝手には進まない |
| コードの見た目 | 本筋にエラー確認が混ざる | 正常系とエラー系を分離できる |
| 伝播 | 毎段で手動で上へ返す必要 | 自動で呼び出し元へさかのぼる |
| コスト | 軽い(ただの分岐) | 投げる瞬間は比較的重い |
「ユーザーが空欄で送信した」のような想定内でよく起きることは、例外より戻り値や検証で素直に表すほうが読みやすく、速いことが多いです。例外は「ファイルが壊れていた」「DB に繋がらない」など、本来は起きないはずの事態に取っておくのが目安。throw を通常のフロー制御(ループ脱出など)に使うのはアンチパターンです。
リソース解放を確実に
ファイル・ネットワーク接続・ロックなどは、処理が成功しても失敗しても必ず閉じる必要があります。途中で例外が飛んでも解放されるよう、finally か、それを言語が肩代わりする仕組み(Python の with、C# / Java の using / try-with-resources)を使います。
# with は、ブロックを抜けるとき(例外時も含めて)自動で close する
with open('data.txt') as f:
data = f.read()
process(data) # ここで例外が出ても f は確実に閉じられる
# ここに来た時点で f はもう閉じている
手で書くなら finally です。try の前ではなく中で開き、finally で閉じるのが定石です。
const conn = openConnection();
try {
conn.query('...');
} finally {
conn.close(); // 成功でも例外でも必ず閉じる
}
握りつぶし禁止(最大のアンチパターン)
最もやってはいけないのが、**捕まえたエラーを握りつぶす(swallow する)**こと。下のように catch で何もしないと、バグが起きても無言で進み続け、後で全く別の場所で原因不明の不具合として現れます。デバッグが地獄になる典型例です。
// アンチパターン:握りつぶし
try {
riskyCall();
} catch (e) {
// 何もしない ← エラーがなかったことにされる
}
捕まえたら最低限、ログに残すか、対処できないなら投げ直す(再スロー)。「ここでは握って正常に続けてよい」と判断したなら、その理由をコメントで残しましょう。
その他、現場でよく刺さるポイント:
- 広すぎる catch を避ける:
catch (Exception)のように何でも捕まえると、想定外のバグまで隠してしまう。捕まえる型はできるだけ具体的に。 - 元の原因を捨てない:投げ直すときは元の例外を「原因(cause)」として連鎖させ、スタックトレースを残す。新しいエラーで上書きして握りつぶさない。
- エラーメッセージは具体的に:「失敗しました」ではなく「ファイル X が見つかりません」など、後から原因を特定できる情報を載せる。
catch や finally の中で新たに例外を投げると、元のエラーが上書きされて消えることがあります(特に finally 内)。後始末のコードこそ、できるだけ安全に・追加の例外を出さないように書くのが鉄則です。
まとめと関連
- 例外処理は「異常を投げ、対処できる場所で捕まえ、後始末は確実に」という分業の仕組み。
- 捕まえるのは対処できる層だけ、解放は
finally/with/usingで確実に、握りつぶしは厳禁。 - 「想定内のこと」は例外ではなく戻り値で表すなど、コストと読みやすさのバランスで使い分ける。
例外を投げたり受けたりするのは関数の境界です。どんなエラーを投げ得るかを型で表現する言語もあり、これは型システムの話題(検査例外やResult型)につながります。また非同期処理では、例外の捕まえ方が同期コードと変わる(Promise の .catch や async/await + try/catch)点にも注意が必要です。
プログラミング Article
例外処理(エラーハンドリング)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
エラーハンドリング
比較で見る軸
難易度: intermediate / カテゴリ: プログラミング / タグ数: 3
導入後に効く点
捕まえなければ呼び出し元へ自動で伝播し、最後まで誰も処理しなければプログラムが落ちる。finally は成功・失敗どちらでも必ず実行される。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- プログラミング
- タグ数
- 3
判断チェックリスト
- 自社の用途が「エラーハンドリング / 例外」に近いか確認する。
- 強みである「例外処理は 異常事態を“投げて(throw)”、対処できる場所で“捕まえる(catch)”仕組み。正常系とエラー系を分けて書ける。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。