Java | Java 標準ライブラリ:Optional.orElse / orElseGet

Java Java
スポンサーリンク

Optional.orElse / orElseGet は「無いときどうするかを決めるメソッド」

Optional は「あるかもしれないし、ないかもしれない値」を入れる箱でした。
では、その箱が「空だったとき」にどうするかを決めるのが orElse / orElseGet です。

雰囲気でいうと、

orElse(T other)
「中身が無ければ、この値を代わりに返して」

orElseGet(Supplier<? extends T> supplier)
「中身が無ければ、この処理を実行して“代わりの値”を作って返して」

という違いです。

パッと見似ていますが、
「代わりの値の作り方(すぐ作るか、必要になってから作るか)」が重要な差になります。


Optional.orElse の基本:もう手元にある“予備の値”を渡す

使いどころのイメージ

orElse は「もう手元に予備の値があるとき」に使うのが自然です。

例えば、「ユーザ名がなければ "ゲスト" にしたい」というケース。

Optional<String> optName = findUserName(userId);  // Optional<String>

String name = optName.orElse("ゲスト");
System.out.println(name);
Java

optName に値が入っていればその値が返り、
空だった場合は "ゲスト" が返ります。

イメージとしては:

  • ある → そのまま使う
  • ない → orElse に渡した値を使う

というシンプルな動きです。

orElse は「引数を必ず評価する」

ここがとても大事なポイントです。

orElse に渡した式は、「Optional に中身があるかどうかに関係なく」
必ず先に評価されてから 渡されます。

例えば、次のコードを見てください。

public String createDefaultName() {
    System.out.println("createDefaultName() 実行");
    return "デフォルト";
}

Optional<String> opt = Optional.of("Taro");

String name = opt.orElse(createDefaultName());
System.out.println("name = " + name);
Java

出力はこうなります。

createDefaultName() 実行
name = Taro
Java

Optional に "Taro" が入っていても、createDefaultName() は呼ばれてしまいます。
呼ばれた結果 "デフォルト" が生成されますが、結局それは捨てられて "Taro" が採用されます。

つまり、orElse の引数は

「中身があってもなくても、とりあえず一回は計算される」

ということです。


Optional.orElseGet の基本:必要になったときだけ“遅延評価”する

orElseGet は関数(ラムダ)を渡す

orElseGet は「中身が無かったときだけ代わりの値を作ってほしい」場合に使います。

さっきの例を orElseGet で書き直します。

String name = opt.orElseGet(() -> createDefaultName());
Java

またはメソッド参照で:

String name = opt.orElseGet(this::createDefaultName);
Java

ここでは () -> createDefaultName() という「引数を取らずに値を返す関数(Supplier)」を渡しています。

動きとしては:

  • Optional に値がある → その値を返し、ラムダは実行されない
  • Optional が空 → ラムダが実行され、その戻り値が採用される

となります。

orElseGet の「本当に必要なときだけ実行される」メリット(重要)

先ほどの例を orElseGet に変えると、出力はこうなります。

Optional<String> opt = Optional.of("Taro");

String name = opt.orElseGet(() -> createDefaultName());
System.out.println("name = " + name);
Java

この場合、createDefaultName() は呼ばれません。
出力は

name = Taro

だけになります。

逆に opt が空のときは、

Optional<String> opt = Optional.empty();

String name = opt.orElseGet(() -> createDefaultName());
System.out.println("name = " + name);
Java

出力は

createDefaultName() 実行
name = デフォルト

のようになります。

ここから分かることは、

orElse → 代わりの値を「先に作ってから」渡す
orElseGet → 代わりの値を「必要になったときだけ作る」

という違いです。


orElse と orElseGet の使い分け方

代わりの値を作るコストが軽いなら orElse で十分

例えば、「単純な固定文字列」を返したいだけなら、
わざわざラムダを書くまでもなく orElse で構いません。

String name = optName.orElse("ゲスト");
Java

"ゲスト" の生成コストはほぼゼロですし、
orElseGet(() -> "ゲスト") と書くと、むしろ読みづらくなります。

このように、「代わりの値の生成が軽くて、書きやすい」場合は
シンプルに orElse で OK です。

重い処理や副作用があるなら orElseGet を選ぶ(重要)

一方で、「代わりの値を作る処理が重い」「副作用がある」場合は orElseGet を使うべきです。

例えば、「デフォルト値を DB や設定ファイルから読み込む」ようなケース。

Optional<String> optConfig = findConfig("timeout");

// 悪い例(どちらにせよ loadDefaultConfig() が呼ばれてしまう)
String value = optConfig.orElse(loadDefaultConfig());

// 良い例(空のときだけ loadDefaultConfig() が実行される)
String value = optConfig.orElseGet(() -> loadDefaultConfig());
Java

loadDefaultConfig()

  • ファイル I/O をする
  • ネットワークにアクセスする
  • ログを出力する

などの「重い/副作用のある処理」だった場合、
値があるのに毎回呼ばれてしまうのは無駄です。

こういうときに、「必要になったときだけ実行する」orElseGet の遅延評価が効いてきます。


もう一歩:orElseGet は「後回しにできる if」のようなもの

if 文で書くとどう見えるか

Optional を使わずに if で書くと、こんな形です。

String value;
if (result != null) {
    value = result;
} else {
    value = loadDefault();
}
Java

これを Optional にすると、次のような対応になります。

String value = Optional.ofNullable(result).orElseGet(() -> loadDefault());
Java

if 版では、result != null なら loadDefault() は呼ばれません。
orElseGet 版も同じく、Optional が空のときだけ loadDefault() が実行されます。

orElse を使っていた場合は、こうなってしまいます。

String value = Optional.ofNullable(result).orElse(loadDefault());
Java

これは if で書き直すと、こういう状態です。

String tmp = loadDefault();           // 先に必ず呼ばれる
String value;
if (result != null) {
    value = result;
} else {
    value = tmp;
}
Java

「結局使わないかもしれないのに、先に loadDefault を実行してしまう if」になっているイメージです。

orElse / orElseGet の違いは、この if で考えると感覚として掴みやすくなります。


まとめ:初心者としての orElse / orElseGet の基準

最後に、頭に残してほしいポイントを整理します。

orElse(value)
「もう手元にあるシンプルな代替値」を使うときに向いている。
その代わり、引数は毎回評価される(重い処理は NG)。

orElseGet(() -> value)
「代替値を“計算して作る”必要がある」場合に向いている。
Optional が空のときだけ、その計算を実行する(遅延評価)。

イメージとしては、

  • 軽くて固定 → orElse
  • 重い・副作用あり・計算が必要 → orElseGet

と覚えておくと、だいたい困りません。

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