Java | 基礎文法:throw

Java Java
スポンサーリンク

throw の全体像

throw は「ここで例外を発生させる」という明示的な合図です。コードが不正な状態や契約違反を検知した瞬間に処理を中断し、例外オブジェクトを投げて呼び出し側へ失敗を伝えます。使いどころは「前提条件のチェック」「状態の矛盾」「外部リソースの異常に意味を付けて再送出」など。正しく使うと、バグを早期に表面化させて、壊れない設計に寄せられます。


throw と throws の違い

役割の違いを一言で

  • throw は「いま例外を起こす」実行文。
  • throws は「このメソッドからその例外が出る可能性がある」宣言。
void f() {
    throw new IllegalArgumentException("不正な入力"); // いま投げる
}

void g() throws java.io.IOException {               // 出る可能性を宣言
    // I/Oで失敗したら IOException が投げられるかもしれない
}
Java

throws を付けても例外は自動で発生しません。実際に失敗したとき、もしくはあなたが throw したときに伝播します。


使い方の基本:前提違反を早期に弾く

入力検証で契約を守る

public static int taxed(int subtotal, double taxRate) {
    if (subtotal < 0) {
        throw new IllegalArgumentException("subtotal must be >= 0: " + subtotal);
    }
    if (taxRate < 0 || taxRate > 1) {
        throw new IllegalArgumentException("taxRate must be 0..1: " + taxRate);
    }
    return (int) Math.round(subtotal * (1 + taxRate));
}
Java

「失敗を後で見つける」より「早く止める」。メッセージに入力値や期待値を含めると、原因特定が圧倒的に速くなります。

null の扱いを明確化

import java.util.Objects;

static String upper(String s) {
    Objects.requireNonNull(s, "s must not be null"); // NullPointerException を投げる
    return s.toUpperCase();
}
Java

契約違反は例外で即座に知らせ、曖昧な状態をシステム内に残さないのが基本方針です。


例外の再送出と包み直し(重要ポイントの深掘り)

原因を連鎖させて意味を付け足す

下位層の具体例外に「文脈」を与え、上位へ伝えるのが包み直しです。原因は必ず渡します。

try {
    dao.save(order);
} catch (java.sql.SQLException e) {
    throw new IllegalStateException("注文保存失敗: id=" + order.id(), e);
}
Java
  • 原因(cause)を渡すことでスタックトレースの連鎖が保たれ、根本原因を辿れます。
  • メッセージには「なぜ」「どの入力で」を含めると、運用で効きます。

同じ型で再スローする

try {
    riskyIO();
} catch (java.io.IOException e) {
    // ログして、責任は上位へ
    System.err.println("I/O失敗: " + e.getMessage());
    throw e;
}
Java

「この層では回復しない」方針なら、再スローで上位の集中ハンドリングに委ねます。


チェック例外と実行時例外に投げ分ける指針(重要ポイントの深掘り)

何を投げるかを設計で決める

  • 外部要因(I/O、ネットワーク、DB)で呼び出し側が回復し得る失敗は「チェック例外」(例:IOException)として throws で宣言して伝える。
  • 契約違反やバグ(不正引数、状態不整合)は「実行時例外」(例:IllegalArgumentException、IllegalStateException、NullPointerException)を throw して即座に表面化させる。

曖昧なときは「呼び出し側が合理的に回復できるか?」を基準に判断します。


カスタム例外を作って意味を伝える

ドメイン固有の失敗を型で表現

public class NotEnoughBalanceException extends RuntimeException {
    public NotEnoughBalanceException(String accountId, int needed, int actual) {
        super("残高不足: id=" + accountId + " needed=" + needed + " actual=" + actual);
    }
}
Java
static void pay(String accountId, int amount, int balance) {
    if (balance < amount) throw new NotEnoughBalanceException(accountId, amount, balance);
    // 決済処理...
}
Java

「型名そのもの」が意味を語るため、呼び出し側は型で分岐でき、メッセージは運用へ効きます。


よくある落とし穴と避け方(重要ポイントの深掘り)

情報のないメッセージ

「Error」「失敗しました」だけでは運用で詰みます。入力値・ID・期待値・状態を要約してメッセージに含めましょう。

原因を捨てる包み直し

// 悪い例:原因 e を渡していない
throw new IllegalStateException("保存失敗");
Java

原因が失われると解析不能になります。必ず new Xxx("msg", e) で連鎖させます。

例外で通常フローを作る

例外は異常専用。通常の分岐や終了条件に例外を使うと可読性・性能が悪化します。前提チェックは if で前倒しに。

広すぎる例外へ投げ替え

「throws Exception」や何でもかんでも RuntimeException へ変換は呼び出し側にとって不親切。意味のある型へ絞り、回復可能性を失わせないようにします。


例題で身につける

例 1: 入力前提違反を即時に通知

public class Price {
    public static int taxed(int subtotal, double taxRate) {
        if (subtotal < 0) {
            throw new IllegalArgumentException("subtotal must be >= 0: " + subtotal);
        }
        if (taxRate < 0 || taxRate > 1) {
            throw new IllegalArgumentException("taxRate must be 0..1: " + taxRate);
        }
        return (int) Math.round(subtotal * (1 + taxRate));
    }
}
Java

例 2: 下位層の失敗を文脈付きで包み直す

class Repository {
    User find(String id) {
        try {
            // DBアクセス...
            return new User(id, "Sato");
        } catch (java.sql.SQLException e) {
            throw new IllegalStateException("ユーザー取得失敗: id=" + id, e);
        }
    }
}
Java

例 3: ルール違反をカスタム例外で明快に

public class OverLimitException extends RuntimeException {
    public OverLimitException(int limit, int actual) {
        super("上限超過: limit=" + limit + " actual=" + actual);
    }
}

static void add(int value, int limit) {
    if (value > limit) throw new OverLimitException(limit, value);
    // 追加処理...
}
Java

仕上げのアドバイス(重要部分のまとめ)

throw は「ここで止める」を宣言するスイッチです。前提違反は実行時例外で早期に弾き、外部要因の失敗はチェック例外として意味を保ったまま上位へ。包み直すときはメッセージに文脈を足し、原因例外を必ず連鎖させる。カスタム例外で型に意味を持たせ、通常フローは if で書く——この型が身につくと、失敗は早く見つかり、直しやすいコードになります。

タイトルとURLをコピーしました