web-dev-qa-db-ja.com

Java 8のオプションの連鎖

存在する最初のオプションが返されるようにオプションを連鎖する方法を探しています。存在しない場合は、Optional.empty()が返されます。

私はこのようないくつかの方法があると仮定します:

Optional<String> find1()

私はそれらを連鎖させようとしています:

Optional<String> result = find1().orElse( this::find2 ).orElse( this::find3 );

ただし、orElseは値を期待し、orElseGetSupplierを期待するため、これは機能しません。

55
piler

ストリームを使用する:

Stream.of(find1(), find2(), find3())
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();

Findメソッドを遅延評価する必要がある場合は、サプライヤ関数を使用します。

Stream.of(this::find1, this::find2, this::find3)
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
80

次のようにできます:

Optional<String> resultOpt = Optional.of(find1()
                                .orElseGet(() -> find2()
                                .orElseGet(() -> find3()
                                .orElseThrow(() -> new WhatEverException()))));

読みやすさのIMOが向上するかどうかはわかりませんが。 Guavaは、オプションを連鎖する方法を提供します。

import com.google.common.base.Optional;

Optional<String> resultOpt = s.find1().or(s.find2()).or(s.find3());

それはあなたの問題の別の選択肢かもしれませんが、JDKの標準のOptionalクラスを使用しません。

標準APIを保持したい場合は、簡単なユーティリティメソッドを記述できます。

static <T> Optional<T> or(Optional<T> first, Optional<T> second) {
    return first.isPresent() ? first : second;
}

その後:

Optional<String> resultOpt = or(s.find1(), or(s.find2(), s.find3()));

チェーンのオプションがたくさんある場合は、すでに述べたように、Streamアプローチを使用する方が良いでしょう。

30
Alexis C.

Sauliの答えに触発されて、flatMap()メソッドを使用することが可能です。

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
  .findFirst();

OptionalをStreamに変換するのは面倒です。どうやら、これは JDK9で修正済み になります。これは次のように書くことができます

Stream.of(this::find1, this::find2, this::find3)
  .map(Supplier::get)
  .flatMap(Optional::stream)
  .findFirst();

Java 9がリリースされた後の更新

元の質問はJava 8、 Optional::or は、Java 9.で導入されました。これにより、問題は次のように解決できます。

Optional<String> result = find1()
  .or(this::find2)
  .or(this::find3);
23
Indrek Ots