はじめに:なぜ独自例外が必要なのか?
JavaにはNullPointerExceptionやIOExceptionなど、たくさんの標準例外が用意されています。
では、「自分で例外クラスを作る意味」って何でしょう?
答えはシンプルです。
エラーを、もっと「意味のある情報」として扱いたいから。
たとえば:
- 在庫が足りない
- ユーザーが見つからない
- 権限がない操作が行われた
こうした「アプリケーション固有のエラー」は、標準の例外では表現しきれません。
だからこそ、独自の例外を使って「何が起きたか」を明確に伝える設計が重要になります。
1. 独自例外を使うメリット
✔ 意味がはっきりする
|
1 2 |
throw new UserNotFoundException("ユーザーが存在しません"); |
このように、何のエラーか一目で分かる。
✔ 呼び出し元での対応が簡単に
|
1 2 3 4 5 6 |
try { userService.login("taro"); } catch (UserNotFoundException e) { // ユーザー登録画面に案内 } |
特定のエラーにだけ対応したいとき、catch文がシンプルになります。
✔ ログやモニタリングに最適
独自例外クラスに「商品ID」「在庫数」などを持たせれば、ログ出力時に具体的な原因追跡が可能になります。
2. 独自例外の基本構文
✔ 最もシンプルな形
|
1 2 3 4 5 6 |
public class MyException extends Exception { public MyException(String message) { super(message); } } |
✔ RuntimeExceptionとして使う(非チェック例外)
|
1 2 3 4 5 6 |
public class MyRuntimeException extends RuntimeException { public MyRuntimeException(String message) { super(message); } } |
3. チェック例外 or 非チェック例外、どっちを継承する?
✔ チェック例外(Exceptionを継承)
- 呼び出し元に「このエラーを必ず扱ってね」と伝えたいとき
- 外部要因によるエラー(DBエラー、ファイル読み込み失敗など)
|
1 2 |
public class DataAccessException extends Exception { ... } |
✔ 非チェック例外(RuntimeExceptionを継承)
- 呼び出し元の設計ミスや、コードのバグが原因のとき
- 引数の不正、状態違反、nullなど
|
1 2 |
public class InvalidInputException extends RuntimeException { ... } |
4. 実務に効く!命名ルールと設計のコツ
| ポイント | 内容 |
|---|---|
| 名前の末尾 | …Exceptionで統一 |
| 処理対象を入れる | UserNotFoundExceptionなど具体的に |
| 極力短く、意味は明確に | InvalidUserAgeExceptionなど長すぎないように |
| カテゴリを分ける | 例:AuthException, ValidationException など |
5. コンストラクタは最低限2つ作ろう
✔ 推奨される2種類
|
1 2 3 4 5 6 7 8 9 10 |
public class UserNotFoundException extends Exception { public UserNotFoundException(String message) { super(message); } public UserNotFoundException(String message, Throwable cause) { super(message, cause); } } |
- メッセージのみ:説明だけ伝えたいとき
- メッセージ+原因例外:別の例外にラップしてスローするときに便利
6. エラーの追加情報をフィールドに持たせる
✔ たとえば、対象のユーザーIDやエラーコードを追加
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
public class UserNotFoundException extends Exception { private final String userId; public UserNotFoundException(String userId) { super("ユーザーが見つかりません: " + userId); this.userId = userId; } public String getUserId() { return userId; } } |
✔ 呼び出し元ではこう使える:
|
1 2 3 4 |
catch (UserNotFoundException e) { log.warn("存在しないユーザーID:" + e.getUserId()); } |
→ デバッグしやすく、モニタリングとも相性抜群です!
7. カスタム例外の使い分けとレイヤー設計
✔ ビジネスロジック層なら
InsufficientStockExceptionUnauthorizedAccessExceptionOrderLimitExceededException
✔ インフラ層(DAOなど)なら
DatabaseConnectionExceptionQueryExecutionException
✔ APIの公開例外として
BadRequestExceptionResourceNotFoundExceptionInternalServerErrorException
→ 責務ごとに分けることで可読性が上がり、役割が明確になります!
8. よくあるアンチパターンに注意!
| アンチパターン | 問題点 |
|---|---|
| 何でも独自例外にする | クラスが増えて複雑化、catch文もごちゃごちゃに |
| ログ出力を例外クラス内に書く | 単一責任の原則に反する(ログはcatch側で) |
| 継承階層が深すぎる | 設計意図が不明に、保守性が下がる |
| cause を渡さない | デバッグしづらくなる |
9. ベストプラクティスまとめ
| 設計項目 | チェックポイント |
|---|---|
| 名前 | 意味が明確・「Exception」で終わる |
| 継承元 | Exception or RuntimeExceptionを明確に |
| コンストラクタ | messageとmessage + causeの2つ |
| 情報追加 | 必要ならIDやコードなどのフィールドを持たせる |
| Javadoc | どんな時にスローされるのか明記する |
| ログ対応 | catchブロックでログ+情報を出力できる設計に |
おわりに:独自例外は「設計力」の証!
独自例外の設計は、単なる「エラー処理」ではありません。
それは、設計思想の表現であり、開発チーム全体の生産性を高める武器です。
- コードを読みやすく
- ログを追いやすく
- バグを減らしやすく
- 利用者に親切に
そんなコードを書けるJavaエンジニアを目指して、まずは基礎をしっかり身につけましょう。
さらに「設計の正解がわからない…」「自分のコードが正しいか不安…」という方には:
👉 絶対にJavaプログラマーになりたい人へ。
で基礎を見直し、
👉 サイゼントアカデミー
でコードレビューや実務的な学びを得るのがオススメです!

コメント