Java | 拡張 for 文を実務で「安全・堅牢」に使う最良の事例集

Java Java
スポンサーリンク

ここでは、拡張 for 文(for-each)を実務で「安全・堅牢」に使うベストプラクティス集を、
実際の業務コードの形で紹介します。

目的は、

「きれいに動く」だけでなく「安全で保守しやすいコード」を書くこと。

(安全設計・例外処理・ロギング対応)


ベストプラクティス①

✅ null安全:コレクションが null のときに安全にスキップする

import java.util.List;
import java.util.Objects;

public class SafeLoopExample {
    public static void main(String[] args) {
        List<String> items = null;

        // nullチェックを入れる
        if (Objects.nonNull(items)) {
            for (String item : items) {
                System.out.println(item);
            }
        } else {
            System.out.println("⚠️ items が null のため処理をスキップしました");
        }
    }
}
Java

🧠 ポイント

  • 実務では「DB結果」「APIレスポンス」が null のことがある。
  • for-eachnull を渡すと NullPointerException
  • Objects.nonNull() / Objects.isNull() で防ぐのがベター。
  • Java 8以降なら Optional.ofNullable(list).orElse(List.of()) でもOK。

ベストプラクティス②

✅ 例外をループ単位で握りつぶさない設計(ログ付き)

import java.util.List;
import java.util.logging.Logger;

public class SafeApiCall {
    private static final Logger logger = Logger.getLogger(SafeApiCall.class.getName());

    public static void main(String[] args) {
        List<String> urls = List.of(
            "https://api.example.com/user",
            "https://api.example.com/order",
            "https://api.example.com/item"
        );

        for (String url : urls) {
            try {
                callApi(url);
                logger.info(() -> "✅ 成功: " + url);
            } catch (Exception e) {
                logger.warning(() -> "⚠️ 失敗: " + url + " → " + e.getMessage());
            }
        }

        logger.info("すべてのAPI呼び出し完了。");
    }

    static void callApi(String url) throws Exception {
        if (url.contains("order")) throw new Exception("Timeout error");
    }
}
Java

🧠 ポイント

  • 拡張 for の中に try-catch を入れるのが定石。
  • 1件失敗してもループ全体を止めない(「部分成功」戦略)。
  • loggerSLF4J / java.util.logging などでOK。
  • 実務では System.out.println より 構造化ロギング が推奨。

ベストプラクティス③

✅ ループ中で例外発生時に再試行(リトライ付き)

import java.util.List;
import java.util.logging.Logger;

public class RetryExample {
    private static final Logger logger = Logger.getLogger(RetryExample.class.getName());

    public static void main(String[] args) {
        List<String> servers = List.of("A", "B", "C");

        for (String server : servers) {
            int retryCount = 0;
            boolean success = false;

            do {
                try {
                    sendData(server);
                    logger.info(() -> "送信成功: " + server);
                    success = true;
                } catch (Exception e) {
                    retryCount++;
                    logger.warning(() -> String.format(
                        "失敗[%s] リトライ回数: %d / 3 - %s", server, retryCount, e.getMessage()
                    ));
                }
            } while (!success && retryCount < 3);

            if (!success) {
                logger.severe(() -> "❌ 3回失敗したためスキップ: " + server);
            }
        }
    }

    static void sendData(String server) throws Exception {
        if (server.equals("B")) throw new Exception("接続エラー");
    }
}
Java

🧠 ポイント

  • 安全な「リトライ+ログ付き」のテンプレート構造。
  • do-while × 拡張 for の組み合わせは実務で非常に多い。
  • **「リトライ上限を必ず設ける」**ことが大切(無限ループ防止)。

ベストプラクティス④

✅ 例外を集約して後でまとめて処理(監査ログ・エラー一覧用)

import java.util.ArrayList;
import java.util.List;

public class AggregateErrors {
    public static void main(String[] args) {
        List<String> files = List.of("data1.csv", "data2.csv", "broken.csv");
        List<String> errors = new ArrayList<>();

        for (String file : files) {
            try {
                processFile(file);
                System.out.println("OK: " + file);
            } catch (Exception e) {
                errors.add(file + " → " + e.getMessage());
            }
        }

        if (!errors.isEmpty()) {
            System.err.println("=== エラー一覧 ===");
            errors.forEach(System.err::println);
        }
    }

    static void processFile(String filename) throws Exception {
        if (filename.contains("broken")) throw new Exception("ファイルが壊れています");
    }
}
Java

🧠 ポイント

  • 業務システムでは「最後にエラーリストをまとめて通知」することが多い。
  • 1件ずつログを出す+最後にサマリーを出す のがよくある設計。
  • errors を別の監査テーブルやメール通知に流す構成にも対応可能。

ベストプラクティス⑤

✅ ループ変数を安全に扱う(書き換えない・再利用しない)

import java.util.List;

public class SafeVariableExample {
    public static void main(String[] args) {
        List<String> users = List.of("Alice", "Bob", "Carol");

        for (String user : users) {
            // NG例:user を直接書き換えても元リストは変わらない
            user = user.toUpperCase(); // ローカル変数だけ変わる
            System.out.println(user);
        }

        System.out.println("元リスト: " + users);
    }
}
Java

出力:

ALICE
BOB
CAROL
元リスト: [Alice, Bob, Carol]

🧠 ポイント

  • for-each の変数は「コピー」です。元データは不変。
  • 元のリストを更新したい場合は: for (int i = 0; i < users.size(); i++) { users.set(i, users.get(i).toUpperCase()); }
  • 読み専用で使うのが基本。

ベストプラクティス⑥

✅ ロギングの粒度を分ける(INFO / WARNING / SEVERE)

実務でログを「あとで追跡できるように」するための設計:

レベル用途例出力例
INFO正常な処理の進行記録「送信成功」「開始」「完了」
WARNING一時的な失敗・再試行・軽微な問題「リトライ1回目失敗」
SEVERE致命的エラー・処理中断「API呼び出し不可」「DB接続失敗」

ベストプラクティス⑦

✅ 早期終了と continue / break の使い分け

for (String s : List.of("A","B","C","STOP","D")) {
    if (s.equals("STOP")) {
        System.out.println("⚠️ STOP に到達、処理終了");
        break; // ループ全体を抜ける
    }
    System.out.println("処理中: " + s);
}

🧠 ポイント

  • 異常値や終了条件が来たら早めに抜ける。
  • 「次だけスキップ」は continue; を使う。
  • 実務では「検査中断」や「入力不正スキップ」に頻出。

総まとめ(実務での安全設計チェックリスト)

チェック項目推奨方法
❌ nullリストの走査✅ nullチェック or Optionalで空リストに置き換え
❌ 例外でループ脱落✅ try-catchをループ内に設ける
❌ 無限リトライ✅ 上限回数+ログ出力
❌ 例外握り潰し✅ ログ or エラーリストに蓄積
❌ 書き換え操作✅ 読み専用で使用、更新は通常for
✅ ログレベルINFO, WARNING, SEVEREで分類管理

実務テンプレ(安全構造の基本形)

この1枚をテンプレとしてプロジェクトで使えます。

for (T item : Optional.ofNullable(items).orElse(List.of())) {
    try {
        process(item);
        logger.info(() -> "処理成功: " + item);
    } catch (RetryableException e) {
        handleRetry(item, e);
    } catch (Exception e) {
        logger.warning(() -> "処理失敗: " + item + " → " + e.getMessage());
        errorList.add(item);
    }
}
Java

Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました