私は新しい Java 8のオプションタイプ で作業しており、機能的にサポートされていない一般的な操作のように見えるものに出くわしました:「orElseOptional」
次のパターンを検討してください。
Optional<Result> resultFromServiceA = serviceA(args);
if (resultFromServiceA.isPresent) return result;
else {
Optional<Result> resultFromServiceB = serviceB(args);
if (resultFromServiceB.isPresent) return resultFromServiceB;
else return serviceC(args);
}
このパターンには多くの形式がありますが、現在のオプションが存在しない場合にのみ呼び出される、新しいオプションを生成する関数を取るオプションの「orElse」が必要になります。
実装は次のようになります。
public Optional<T> orElse(Supplier<Optional<? extends T>> otherSupplier) {
return value != null ? this : other.get();
}
そのような方法が存在しない理由があるのか、意図しない方法でOptionalを使用しているのか、そしてこのケースに対処するために人々が思いついた他の方法があるのか、興味があります。
私は、カスタムユーティリティクラス/メソッドを含むソリューションはエレガントではないと思うべきです。なぜなら、私のコードで作業している人は、それらが存在することを必ずしも知らないからです。
また、誰かが知っている場合、そのようなメソッドはJDK 9に含まれますか?そのようなメソッドはどこで提案できますか?これは、私にとってAPIのかなり目立たないように思えます。
現在のAPIを前提とした最もクリーンな「サービスの試行」アプローチは次のとおりです。
Optional<Result> o = Stream.<Supplier<Optional<Result>>>of(
()->serviceA(args),
()->serviceB(args),
()->serviceC(args),
()->serviceD(args))
.map(Supplier::get)
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst();
重要な側面は、一度書く必要がある(一定の)操作の連鎖ではなく、別のサービスを追加する(またはサービスのリストを変更するのが一般的)ことの簡単さです。ここでは、単一の()->serviceX(args)
を追加または削除するだけで十分です。
ストリームの遅延評価のため、先行するサービスが空でないOptional
を返した場合、サービスは呼び出されません。
それはきれいではありませんが、これは動作します:
return serviceA(args)
.map(Optional::of).orElseGet(() -> serviceB(args))
.map(Optional::of).orElseGet(() -> serviceC(args))
.map(Optional::of).orElseGet(() -> serviceD(args));
.map(func).orElseGet(sup)
は、Optional
で使用するのに非常に便利なパターンです。これは、「このOptional
に値v
が含まれる場合、func(v)
を提供し、そうでない場合はsup.get()
を提供する」ことを意味します。
この場合、serviceA(args)
を呼び出してOptional<Result>
を取得します。そのOptional
に値v
が含まれている場合、Optional.of(v)
を取得しますが、空の場合は、serviceB(args)
を取得します。より多くの選択肢でリンスを繰り返します。
このパターンの他の用途は次のとおりです。
.map(Stream::of).orElseGet(Stream::empty)
.map(Collections::singleton).orElseGet(Collections::emptySet)
おそらくこれはあなたが望んでいることです: 1つのオプションまたは別のものから値を取得する
それ以外の場合は、 Optional.orElseGet
をご覧ください。ここに、私があなたが望んでいることを考える例を示します:
result = Optional.ofNullable(serviceA().orElseGet(
() -> serviceB().orElseGet(
() -> serviceC().orElse(null))));
まだJDK8を使用していると仮定すると、いくつかのオプションがあります。
例えば。:
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);
}
}
あなたができるように:
return Optionals.or(
()-> serviceA(args),
()-> serviceB(args),
()-> serviceC(args),
()-> serviceD(args)
);
例えば。 google guavaのOptionalは適切なor()
操作をサポートします(JDK9と同様)。例:
return serviceA(args)
.or(() -> serviceB(args))
.or(() -> serviceC(args))
.or(() -> serviceD(args));
(各サービスがcom.google.common.base.Optional
ではなくJava.util.Optional
を返す場所)。
これは、パターンマッチングと、SomeおよびNone実装( Javaslang 、 FunctionalJava )または怠zyな Maybe cyclopsの実装 -react 。このライブラリの作成者です。
cyclops-react を使用すると、JDKタイプで構造的 パターンマッチング を使用することもできます。 Optionalでは、 visitor pattern を使用して、現在のケースと不在のケースを照合できます。これは次のようになります-
import static com.aol.cyclops.Matchables.optional;
optional(serviceA(args)).visit(some -> some ,
() -> optional(serviceB(args)).visit(some -> some,
() -> serviceC(args)));