web-dev-qa-db-ja.com

CompletableFuture | thenApply vs thenCompose

thenApply()とthenCompose()の違いを理解できません。

だから、誰かが有効なユースケースを提供できますか?

Javaドキュメントから:

thenApply(Function<? super T,? extends U> fn)

このステージが正常に完了すると、指定された関数への引数としてこのステージの結果で実行される新しいCompletionStageを返します。

thenCompose(Function<? super T,? extends CompletionStage<U>> fn)

このステージが正常に完了すると、指定された関数への引数としてこのステージで実行される新しいCompletionStageを返します。

thenComposeの2番目の引数は、thenApplyが拡張しないCompletionStageを拡張することがわかります。

誰かが例を提供できますか?その場合、thenApplyを使用する必要があり、thenComposeを使用する必要がありますか?

81
GuyT

thenApplyは、同期マッピング関数がある場合に使用されます。

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenApply(x -> x+1);

thenComposeは、非同期マッピング関数(つまり、CompletableFutureを返す関数)がある場合に使用されます。その後、ネストされたFutureではなく、結果を含むFutureを直接返します。

CompletableFuture<Integer> future = 
    CompletableFuture.supplyAsync(() -> 1)
                     .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1));
117
Joe C

Java 9の更新されたJavadocは、おそらくそれをよりよく理解するのに役立つでしょう:

thenApply

<U> CompletionStage<U> thenApply​(Function<? super T,? extends U> fn)

新しい CompletionStage を返します。このステージが正常に完了すると、このステージの結果が提供された関数の引数として実行されます。

このメソッドは Optional.map および Stream.map に類似しています。

例外的な補完に関するルールについては、 CompletionStage のドキュメントをご覧ください。

thenCompose

<U> CompletionStage<U> thenCompose​(Function<? super T,? extends CompletionStage<U>> fn)

新しい CompletionStage を返します。これは、指定された関数によって返されたCompletionStageと同じ値で完了します。

このステージが正常に完了すると、このステージの結果を引数として指定された関数が呼び出され、別のCompletionStageが返されます。そのステージが正常に完了すると、このメソッドによって返されるCompletionStageは同じ値で完了します。

進行を保証するために、提供された関数は結果の最終的な完了を手配する必要があります。

このメソッドは Optional.flatMap および Stream.flatMap に類似しています。

例外的な補完に関するルールについては、 CompletionStage のドキュメントをご覧ください。

37
Didier L

@Joe Cが投稿した回答は誤解を招くと思います。

thenApplythenComposeの違いを例で説明してみましょう。

getUserInfo(int userId)getUserRating(UserInfo userInfo)の2つのメソッドがあるとします:

public CompletableFuture<UserInfo> userInfo = getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

メソッドの戻り値の型は両方ともCompletableFutureです。

最初にgetUserInfo()を呼び出し、その完了時に、結果のUserInfogetUserRating()を呼び出します。

getUserInfo()メソッドが完了したら、thenApplythenComposeの両方を試してみましょう。違いは戻り型にあります。

CompletableFuture<CompletableFuture<UserRating>> f =
    userInfo.thenApply(this::getUserRating);

CompletableFuture<UserRating> relevanceFuture =
    userInfo.thenCompose(this::getUserRating);

thenCompose()ScalaのflatMap のように機能し、ネストされた先物をフラット化します。

thenApply()はネストされたfutureをそのまま返しましたが、thenCompose()はネストされたCompletableFuturesをフラット化したため、より多くのメソッド呼び出しをチェーン化することが容易になりました。

23
Dorjee

thenApplyおよびthenComposeは、CompletableFutureで呼び出され、Functionを指定することで、その結果を処理します。 thenApplythenComposeは、両方とも自分自身の結果としてCompletableFutureを返します。そのため、複数のthenApplyまたはthenComposeをチェーンでき、それぞれが最後のFunctionの結果に対して何かを行うFunctionを持ちます。

このFunctionは、同期的に何かを行う必要があり、結果を返すことがあります。その場合はthenApplyを使用する必要があります。

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously
    .thenApply((x)->System.println(x));

また、このFunctionで何か非同期を行うこともできます。この非同期のことはCompletionStageを返す必要があります。チェーン内の次のFunctionは、入力としてCompletionStageを取得するのではなく、そのCompletionStageの結果に関心があります。したがって、thenComposeを使用する必要があります。

// addOneAsync may be implemented by using another thread, or calling a remote method
// CompletableFuture<Integer> addOneAsync(int input);
CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous
    .thenApply((x)->System.println(x));

JavaScriptでは、Promise.thenは、値または値のPromiseを返す関数を受け入れることができます。 Javaでは、型の規則により、2つの関数を明確に型付けする必要があります。 (Function<? super T,? extends U> fnおよびFunction<? super T,? extends CompletionStage<U>> fn。 (または、JavaはCompletionStageを返す場合、特別なタイプのチェックを行う必要がありますが、前者を選択します)最終結果であるPromise.thenは、2つの部分thenApplythenComposeで実装されます。

my answer about thenApplyAsyncが読みにくい場合は、これも読むことができます。

7
1283822