Java:Optional#orElse、orElseGet
Optionalにおいて値が無い場合にどうするかなのだが、orElse、orElseGetというのがある。
どちらを使うべきか?
これを検索してみると。「常にorElseGetを使うべき」などとしているところがあるのだが、もちろんそれでも良いのだが、全く「そもそも」論が抜けている。基本的な原理を考えずに、「うまく行くから」という理由でorElseGetを勧めているに過ぎない。例えばこんな例を使っている。
Optional<String>opt = Optional.empty();
String result = opt.orElse(getSomeString());
String getSomeString() {
return "foo";
}
そして、「optが空でなくてもgetSomeString()が呼び出されてしまうから」と言っているのだが、当たり前だ。
最終的にresultに返される値は、orElseメソッドの実行結果なのである。したがって、orElseの引数は「必ず実行される」。orElseが中で何をやっていようが関係無い。引数に(定数ではなく)式やらメソッド呼び出しやらが記述されていれば、それは必ず実行されるのである。これはorElseの仕様などではなく、Java言語、あるいは他の言語でも全く同じ普遍的な仕様だ。
その一方で、
String result = opt.orElseGet(()->getSomeString());
とした場合、引数に与えられるのはラムダ式であり、必要になるまでは、getSomeString()が呼び出されないという、それだけのことだ。
orElse、orElseGetのカスケーディング
この項目を書いた理由というのは、orElse、orElseGetのカスケーディングをどのようにすべきかなのだが、本題の前に前置きが長くなってしまった。
お題というのはこうである。
- あるOptionalがあるが、値が空だった場合は、何らかの値を得る努力をする(他メソッド呼び出しなど)。しかしそれでも空の場合がある。
- 最終的に空の場合でもnullは扱いたくない。
ということである。単純に考えるとこうだ。
Optional<String>opt = Optional.empty();
String result = opt.orElseGet(()->getSomeString());
String getSomeString() {
return "foo"; // nullが返される場合もある
}
この場合、resultがnullかどうかを判定しなくてはいけない。単純な解決法としては以下だ。
Optional<String> result = Optional.ofNullable(opt.orElseGet(()->getSomeString()));
あまり美しくない。
Stackoverflowにおける議論
Stackoverflowに議論があった。
Java9のorを使う
Java9からはorというメソッドが用意されている。これを使った場合はこうする。
Optional<String>opt = Optional.empty();
Optional<String>result = opt.or(()->getSomeString());
Optional<String>getSomeString() {
return Optional.of("foo"); // あるいはOptional.empty()
}
もちろんこれは、次々に接続していくこともできるだろう。
opt
.or(()->...)
.or(()->...)
....
自前のユーティリティを作ってしまう
Java8でも可能な解決策として、次のようなユーティリティを作ってしまう方法がある。
import java.util.*;
import java.util.function.*;
public class Optionals {
static <T> Optional<T> or(Supplier<Optional<T>>... optionals) {
return Arrays.stream(optionals)
.map(Supplier::get)
.filter(Optional::isPresent)
.findFirst()
.orElseGet(Optional::empty);
}
}
これを使うと、複数のOptionalから最初のものを選択することができる。
Optionals.or(
()->opt,
()-> getSomeString(),
.....
);
他の解決策も提示されているが、個人的にはこの二つで十分かと思う。