JavaのOptional.orElseとorElseGetの違いは、代替値を作る処理をいつ実行するかです。orElse(fallback())はOptionalに値があってもfallback()の呼び出し式を先に評価します。一方、orElseGet(() -> fallback())は値がないときだけSupplierを呼び出します。重い処理や副作用のある処理を代替値に使うなら、orElseGetを検討してください。
orElseとorElseGetの違いを最初に比較
| 観点 | orElse(T other) | orElseGet(Supplier) |
|---|---|---|
| 引数 | 代替値そのもの | 代替値を作る関数 |
| 値がある場合 | Optional内の値を返す。引数式の評価はすでに済んでいる | Optional内の値を返す。Supplierを呼ばない |
| 値がない場合 | 評価済みのotherを返す | Supplierを実行して結果を返す |
| 向く用途 | 固定文字列や軽い定数 | DB呼び出し、計算、ログ付きの代替値生成 |
違いの中心は「Optionalの中身を選ぶ順番」ではなく、「メソッドを呼ぶ前の引数評価」と「Supplierの遅延実行」です。
用語解説:Supplier<T>
値を引数なしで生成して返すためのJavaの関数型インターフェースです。orElseGetへ渡すと、Optionalが空の場合にget()経由で呼び出されます。
値があるのに代替処理が走る理由
Javaでは通常、メソッドを呼ぶ前に引数式を評価します。したがってorElse(makeFallback())と書くと、Optionalが空かどうかに関係なくmakeFallback()が先に実行されます。
import java.util.Optional;
public class OptionalSample {
static String makeFallback() {
System.out.println("代替値を生成");
return "guest";
}
public static void main(String[] args) {
Optional<String> name = Optional.of("Alice");
String a = name.orElse(makeFallback());
String b = name.orElseGet(() -> makeFallback());
System.out.println("orElse: " + a);
System.out.println("orElseGet: " + b);
}
}
この例ではmakeFallback()はorElseの引数式で1回だけ実行されます。nameはAliceを持つため、orElseGetのSupplier側は呼び出されません。実行を確認する場合はコンソール出力を比較してください。
Optionalが空の場合は両方とも代替値を返す
Optional<String> empty = Optional.empty();
String first = empty.orElse("guest");
String second = empty.orElseGet(() -> "guest");
System.out.println(first);
System.out.println(second);
この場合はどちらもguestになります。Optionalが空の場合、orElseGetのSupplierは実行されます。処理結果だけを見ると同じですが、代替値を作るタイミングが異なります。
固定値ならorElseでよい
代替値に単なる文字列や既に作成済みの変数を渡すなら、orElseを選ぶ方が読みやすい場合があります。すべてのorElseをorElseGetに書き換えれば速くなるわけではありません。
Optional<String> nickname = Optional.empty();
String shown = nickname.orElse("未設定");
一方、ファイル読み込み・データベースアクセス・オブジェクト生成・計算などが代替値にある場合、値が存在していても不要な仕事が起きていないか確認しましょう。
代替値の生成が重いならorElseGetを使う
import java.util.Optional;
class ProfileExample {
static String loadDefaultProfile() {
// 実際には設定やDBなどから取得する処理を想定
return "default-profile";
}
static String showProfile(Optional<String> profile) {
return profile.orElseGet(ProfileExample::loadDefaultProfile);
}
}
このコードではprofileに値があればloadDefaultProfile()を呼び出しません。メソッド参照は実行を遅らせるSupplierとして働きます。
ただしorElseGetの中で副作用のある操作をすると、Optionalが空のときにその副作用は実行されます。ログ記録、書き込み処理、外部リクエストなどは、代替処理として本当に適切かを先に確認してください。
よくある書き間違いと例外
- orElseGet(makeFallback())のように値を直接渡すと、Supplier<T>を要求するため通常は型が合いません。
- orElseGet(() -> makeFallback())はSupplierを渡す正しい形です。
- Optionalの中身がnullになる想定ではなく、ofNullable(null)で空Optionalを作るのが一般的です。
- 必須値がないなら代替値よりorElseThrow()が適切な場合もあります。
- nullのSupplierを渡すとOptionalが空の場合にNullPointerExceptionになるため避けます。
Optional<String> value = Optional.ofNullable(null);
String selected = value.orElseGet(() -> "guest");
Optional<String> required = Optional.empty();
// required.orElseThrow(); // 必須値なしを例外にする例
Optionalの結果が必要なのか、単なるデフォルト値が必要なのか、空なら例外にすべきなのかを区別するとAPIを選びやすくなります。
orElseGetが速いと決めつけない
「必ずorElseGetのほうが高速」と断定することはできません。代替値の計算量や実行頻度、JVMの最適化、実際のボトルネックによって差は変わります。
| シーン | 判断 |
|---|---|
| 固定文字列を返す | orElseが簡潔 |
| 代替値の生成にI/Oがある | orElseGetで不要な処理を回避 |
| 副作用を避けたい | どの条件で実行されるかを明示 |
| 値がなければ処理を中止する | orElseThrowの用途も確認 |
コードレビューでは「代替値の式がいつ評価されるか」と「副作用が許容されるか」を主に確認し、性能差は必要に応じて測定します。
関連するJavaの記事
Optionalはnullを扱うときの設計判断にも関係します。NullPointerExceptionの発生経路を確認したい場合は、Spring Bootでのnull対策の記事が役立ちます。また関数型の書き方に慣れるにはStreamの記事が参考になります。
(JavaとSpring Bootのnull対策については『Spring Boot NullPointerException完全対策|3大原因と実装例まとめ』をご参照ください)
(Javaの関数型処理の基礎については『Javaのfor文をStreamに書き換えるには?Spring Bootでの実践例付きでわかる!』をご参照ください)
よくある疑問とまとめ
orElseGetには必ずラムダ式が必要?
ラムダ式だけでなく、Supplier<T>に一致するメソッド参照も渡せます。
orElseは値があっても代替値を返す?
いいえ。Optionalに値があればその値を返します。ただしorElseへ渡す引数の計算は呼び出し前に行われます。
orElseGetでも副作用が発生する?
Optionalが空ならSupplierが実行されるため、副作用のある処理を入れれば発生します。
Optional.orElseとorElseGetは、どちらも値がない場合の代替値を扱うAPIです。代替値の生成が簡単な定数ならorElse、重い処理や副作用を伴う生成ならorElseGetの遅延実行が役立ちます。
代替値をメソッドで生成している箇所を見つけ、Optionalに値があるときも実行されてよいか確認してみてください。