early return の全体像
early return(早期リターン)は、関数の冒頭で「前提違反・エラー・不要条件」をすぐに判定して、処理を中断し、直ちに戻り値を返す書き方です。深い if/else の入れ子を避けて、主要ロジックを「まっすぐ読める」形にします。目的は可読性を上げ、バグを減らし、意図を明確にすること。ガード節とも呼ばれます。
使い方の基本と効果
ガード節で前提を固定する
入口で不正な入力や状態を弾くと、後続のコードは「前提が満たされている世界」だけを書けばよくなります。否定条件を上側で処理し、肯定の本筋を下側に一直線で置くのがコツです。
int price(int qty) {
if (qty <= 0) return 0; // 無効値を早期に処理
if (qty < 10) return 100 * qty;
return 90 * qty; // デフォルト分岐
}
Java早期リターンを使うと else が減り、分岐の深さが浅く保たれます。読む人は「何を満たせば進むか」を上から順番に理解できます。
入力検証と契約の明確化
メソッドの契約として null 禁止や範囲制約があるなら、最初に検証して即座に止めます。曖昧な状態を内部に持ち込まないのが基本方針です。
import java.util.Objects;
String normalizedUpper(String s) {
Objects.requireNonNull(s, "s must not be null"); // 契約違反を即座に表面化
if (s.isBlank()) return ""; // 早期に既定値へ
return s.trim().toUpperCase(); // 本筋
}
Java重要ポイントの深掘り:ネスト削減と意図の可視化
深い入れ子からの脱出
早期リターンは「否定条件を先に弾く」テクニックです。深い if の中で重ねて判定するより、上から順に振り分けたほうが理解が速くなります。
// Before(読みづらい入れ子)
if (user != null) {
if (user.isActive()) {
if (!user.isBanned()) {
sendMail(user);
}
}
}
// After(早期リターンで直線化)
if (user == null) return;
if (!user.isActive()) return;
if (user.isBanned()) return;
sendMail(user);
Javaこの形にすると「通過条件のリスト」がそのままコードになり、レビューや修正が容易になります。
早期終了で計算量と副作用を抑える
条件を満たさない場合に即座に抜けることで、無駄な処理を避けられます。複数段の重いチェックは軽いものを先に配置すると、全体効率が上がります。
boolean process(Token token) {
if (token == null) return false; // 軽いチェック
if (!isValid(token)) return false; // 中程度
if (!hasPermission(token)) return false; // 重いチェック
doWork(token);
return true;
}
Javaエラーハンドリング・後始末との組み合わせ
早期リターンと try-finally の両立
後始末が必要な場合は、必ず finally で行います。早期リターンしても finally は実行されるため、安全にクリーンアップできます。
Resource r = acquire();
try {
if (!r.isReady()) return; // 早期終了
use(r);
} finally {
r.close(); // 必ず解放
}
Javaリソース管理では try-with-resources を使うと、後始末を自動化しつつ早期リターンも安全に書けます。
try (var br = java.nio.file.Files.newBufferedReader(java.nio.file.Path.of("a.txt"))) {
String line = br.readLine();
if (line == null) return; // 早期終了
System.out.println(line);
}
Javaよくある疑問と注意点
「return が多いと悪い?」への答え
「どこで戻るかが明確」で「後始末が適切に担保されている」なら、早期リターンはむしろ読みやすさと安全性を高めます。問題になるのは、戻りどころが散らばり過ぎて意図が見えない場合です。入口のガード節に集約し、主要ロジックは一本にすると迷いません。
例外と早期リターンの使い分け
契約違反やプログラムのバグには例外を使い、想定内の条件不成立(空入力、該当なしなど)は早期リターンで扱うと、意図が明確になります。
User findOrNull(String id) {
if (id == null || id.isBlank()) return null; // 想定内の不成立
var user = repository.find(id);
return user.orElse(null);
}
User findOrThrow(String id) {
if (id == null || id.isBlank()) throw new IllegalArgumentException("id must not be blank"); // 契約違反
return repository.find(id).orElseThrow(() -> new IllegalStateException("not found: " + id));
}
Java例題で身につける
例 1: 早期リターンで入れ子を潰す
int fee(String plan, int qty) {
if (qty <= 0) return 0; // ガード
if (plan == null) return 100 * qty;
if (plan.equals("PRO")) return 90 * qty;
return 100 * qty; // デフォルト
}
Java読み手は上から「通過条件」を追えばよく、主要計算が一番下にまとまります。
例 2: バリデーションを入口で終わらせる
int taxed(int subtotal, double rate) {
if (subtotal < 0) throw new IllegalArgumentException("subtotal must be >= 0");
if (rate < 0 || rate > 1) throw new IllegalArgumentException("rate must be 0..1");
return (int) Math.round(subtotal * (1 + rate)); // 本筋
}
Java契約違反は即時に止め、主要計算から「防御コード」を追い出します。
例 3: 早期リターンと try-with-resources
import java.nio.file.*;
import java.io.*;
String firstLineOrEmpty(Path p) throws IOException {
try (var br = Files.newBufferedReader(p)) {
String line = br.readLine();
if (line == null || line.isBlank()) return ""; // 早期終了+既定値
return line.trim();
}
}
Java後始末は自動、分岐は冒頭で終了させて本筋を短く保っています。
仕上げのアドバイス(重要部分のまとめ)
early return は「否定条件を入口で弾き、主要ロジックを一本化」するための設計技法です。ガード節で前提を固定し、else を減らして直線化する。重い処理は軽いチェックの後に置き、不要な計算を早期に避ける。リソース管理では finally や try-with-resourcesと組み合わせて安全に終了させる。例外は契約違反に、早期リターンは想定内の不成立に——この使い分けが身につけば、コードは読みやすく、変更にも強くなります。
