これら2つのメソッドOptional.flatMap()
とOptional.map()
の違いは何ですか?
一例をいただければ幸いです。
関数が必要なオブジェクトを返す場合はmap
を使用し、関数がflatMap
を返す場合はOptional
を使用します。例えば:
public static void main(String[] args) {
Optional<String> s = Optional.of("input");
System.out.println(s.map(Test::getOutput));
System.out.println(s.flatMap(Test::getOutputOpt));
}
static String getOutput(String input) {
return input == null ? null : "output for " + input;
}
static Optional<String> getOutputOpt(String input) {
return input == null ? Optional.empty() : Optional.of("output for " + input);
}
両方の印刷ステートメントは同じものを印刷します。
どちらもオプションのタイプから何かに機能を取ります。
map()
は、オプションの「as(」を適用します:
if (optional.isEmpty()) return Optional.empty();
else return Optional.of(f(optional.get()));
関数がT -> Optional<U>
の関数である場合はどうなりますか?
結果はOptional<Optional<U>>
です!
flatMap()
の目的は次のとおりです。関数が既にOptional
を返している場合、flatMap()
は少し賢く、二重にラップせずにOptional<U>
を返します。
これは、2つの機能的なイディオム、map
およびflatten
の構成です。
注:-以下は、mapおよびflatmap関数の図です。それ以外の場合、Optionalは主に戻り値の型としてのみ使用されるように設計されています。
既にご存知かもしれませんが、Optionalは単一のオブジェクトを含む場合も含まない場合もある一種のコンテナーであるため、null値が予想される場所ならどこでも使用できます(Optionalを正しく使用すると、NPEが表示されない場合があります)。たとえば、nullを許可する人物オブジェクトを期待するメソッドがある場合、次のようなメソッドを記述できます。
void doSome(Optional<Person> person){
/*and here you want to retrieve some property phone out of person
you may write something like this:
*/
Optional<String> phone = person.map((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
class Person{
private String phone;
//setter, getters
}
ここでは、オプションの型に自動的にラップされるString型を返しました。
個人クラスがこのように見える場合、つまり電話もオプションです
class Person{
private Optional<String> phone;
//setter,getter
}
この場合、マップ関数を呼び出すと、戻り値がOptionalにラップされ、次のような結果が得られます。
Optional<Optional<String>>
//And you may want Optional<String> instead, here comes flatMap
void doSome(Optional<Person> person){
Optional<String> phone = person.flatMap((p)->p.getPhone());
phone.ifPresent((ph)->dial(ph));
}
PS; NullPointerExceptionsなしでは生きられない場合を除き、isPresent()でチェックせずにOptionalでgetメソッド(必要な場合)を呼び出さないでください。
私が役立ったのは、2つの関数のソースコードを見ていたことです。
Map-オプションで結果をラップします。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value)); //<--- wraps in an optional
}
}
flatMap-'raw'オブジェクトを返します
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value)); //<--- returns 'raw' object
}
}
Optional.map()
:すべての要素を受け取り、値が存在する場合は、関数に渡されます。
Optional<T> optionalValue = ...;
Optional<Boolean> added = optionalValue.map(results::add);
現在、追加には3つの値のいずれかがあります:true
またはfalse
は、Optionalの場合、optionalValue
または、empty Optional.
結果を処理する必要がない場合は、単にifPresent()
を使用できますが、戻り値はありません。
optionalValue.ifPresent(results::add);
Optional.flatMap()
:ストリームの同じ方法と同様に機能します。ストリームのストリームを平坦化します。値が提示された場合、関数に適用されるという違いがあります。それ以外の場合、空のオプションが返されます。
オプションの値関数呼び出しの作成に使用できます。
メソッドがあるとします:
public static Optional<Double> inverse(Double x) {
return x == 0 ? Optional.empty() : Optional.of(1 / x);
}
public static Optional<Double> squareRoot(Double x) {
return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
}
次に、次のように逆行列の平方根を計算できます。
Optional<Double> result = inverse(-4.0).flatMap(MyMath::squareRoot);
または、必要に応じて:
Optional<Double> result = Optional.of(-4.0).flatMap(MyMath::inverse).flatMap(MyMath::squareRoot);
inverse()
またはsquareRoot()
がOptional.empty()
を返す場合、結果は空です。