Комплектація майбутнього | то Застосувати проти, тоді скласти


119

Я не можу голову навколо різниці між thenApply() і thenCompose().

Отже, чи може хтось надати справжній випадок використання?

З документів Java:

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

Повертає нове, CompletionStageяке, коли цей етап нормально завершується, виконується з результатом цього етапу як аргументом наданої функції.

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

Повертає нове, CompletionStageяке, коли цей етап нормально завершується, виконується з цим етапом як аргумент для наданої функції.

Я розумію, що другий аргумент thenComposeрозширює CompletionStage там, де thenApplyйого немає.

Чи міг би хтось навести приклад, у якому випадку я повинен використовуватись thenApplyі коли thenCompose?


39
Ви розумієте різницю між mapі flatMapв Stream? thenApplyце mapі thenComposeє flatMapз CompletableFuture. Ви використовуєте, thenComposeщоб не мати CompletableFuture<CompletableFuture<..>>.
Міша

2
@Misha Дякую за ваш коментар Так, я знаю різницю між mapі, flatMapі я розумію вашу думку. Ще раз дякую :)
GuyT

Це дуже приємний посібник для початку з CompletableFuture - baeldung.com/java-completablefuture
thealchemist

Відповіді:


168

thenApply використовується, якщо у вас є функція синхронного відображення.

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

thenComposeвикористовується, якщо у вас є функція асинхронного відображення (тобто така, яка повертає a CompletableFuture). Тоді воно поверне майбутнє з результатом безпосередньо, а не вкладене майбутнє.

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

14
Чому програміст повинен використовувати .thenCompose(x -> CompletableFuture.supplyAsync(() -> x+1))замість .thenApplyAsync(x -> x+1)? Будучи синхронним або асинхронним це НЕ відповідна різниця.
Холгер

17
Вони б не робили так. Однак, якби стороння бібліотека, яку вони використовували, повернула a CompletableFuture, тоді це був thenComposeби спосіб вирівняти структуру.
Джо С

1
@ArunavSanyal, голоси показують іншу картину. Ця відповідь є чіткою та стислою.
Олексій Шестеров

@Holger прочитав мою іншу відповідь, якщо ти збентежився, thenApplyAsyncбо це не те, що ти думаєш.
1283822

@ 1283822 Я не знаю, що змушує вас думати, що я був розгублений, і у вашій відповіді немає нічого, що підтверджує ваше твердження, що "це не те, що ви думаєте, що є".
Холгер

49

Я думаю, що відповідь, опублікована @Joe C, є оманливою.

Дозвольте спробувати пояснити різницю між thenApplyі thenComposeна прикладі.

Припустимо, що у нас є 2 методи: getUserInfo(int userId)і getUserRating(UserInfo userInfo):

public CompletableFuture<UserInfo> getUserInfo(userId)

public CompletableFuture<UserRating> getUserRating(UserInfo)

Обидва типи повернення є CompletableFuture.

Ми хочемо зателефонувати getUserInfo()спочатку, а по його завершенні подзвонити getUserRating()з отриманим UserInfo.

Після завершення getUserInfo()методу спробуємо і те, thenApplyі інше thenCompose. Різниця полягає у типах повернення:

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

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

thenCompose()працює на зразок Scala,flatMap яка згладжує вкладені ф'ючерси.

thenApply()повернув вкладені ф'ючерси такими, якими вони були, але thenCompose()розрівняв вкладені CompletableFuturesтак, щоб було легше пов'язати більше викликів методів до нього.


2
Тоді відповідь Джо С не вводить в оману. Це правильно і більш стисло.
koleS

2
Це було дуже корисно :-)
dstibbe

1
Так як це поганий дизайн, щоб написати CompletableFuture <UserInfo> getUserInfo та CompletableFuture <UserRating> getUserRating (UserInfo) \\, замість цього має бути UserInfo getUserInfo () та int getUserRating (UserInfo), якщо я хочу використовувати його для асинхронізації та ланцюга, тоді я можу використовуйте ompletableFuture.supplyAsync (x => getUserInfo (userId)). ТодіApply (userInfo => getUserRating (userInfo)) або щось подібне, це більш читабельний imho, і не обов'язковий для загортання ВСІХ типів повернення в CompletableFuture
user1694306

@ user1694306 Це погана конструкція чи ні, залежить від того, чи міститься рейтинг користувача у UserInfo(тоді так) чи потрібно його отримувати окремо, можливо, навіть дорого (тоді ні).
glglgl

42

Оновлений Javadocs у Java 9, ймовірно, допоможе зрозуміти це краще:

потімЗастосувати

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

Повертає нове, CompletionStageяке, коли цей етап нормально завершується, виконується з результатом цього етапу як аргументом наданої функції.

Цей метод є аналогом Optional.mapі Stream.map.

Дивіться CompletionStageдокументацію щодо правил, що стосуються виключного завершення.

потімскладіть

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

Повертає нове, CompletionStageщо завершується тим самим значенням, що і CompletionStageповернута даною функцією.

Коли цей етап завершується нормально, дана функція викликається результатом цього етапу як аргумент, повертаючи інший CompletionStage. Коли цей етап завершується нормально, CompletionStageповертається цим методом завершується тим же значенням.

Щоб забезпечити прогрес, надана функція повинна організувати можливе завершення її результату.

Цей метод є аналогом Optional.flatMapі Stream.flatMap.

Дивіться CompletionStageдокументацію щодо правил, що стосуються виключного завершення.


14
Цікаво , чому вони не назвали ці функції mapі flatMapв першу чергу.
Маттіас Браун

1
@MatthiasBraun Я думаю, що це тому thenApply(), що просто подзвонить Function.apply(), і thenCompose()трохи схожий на функції композиції.
Didier L

14

thenApplyі thenComposeє методами CompletableFuture. Використовуйте їх тоді, коли ви збираєтесь щось зробити CompleteableFutureз результатом Function.

thenApplyі thenComposeобидва повертають a CompletableFutureяк власний результат. Ви можете з'єднати кілька thenApplyабо thenComposeразом. Введіть a Functionна кожен виклик, результатом якого буде вхід до наступного Function.

Поставленому Functionвами інколи потрібно щось робити синхронно. Тип повернення вашого Functionмає бути нетиповим Future. У цьому випадку вам слід скористатися thenApply.

CompletableFuture.completedFuture(1)
    .thenApply((x)->x+1) // adding one to the result synchronously, returns int
    .thenApply((y)->System.println(y)); // value of y is 1 + 1 = 2

В іншому випадку ви можете зайнятися асинхронною обробкою в цьому Function. У такому випадку вам слід скористатися thenCompose. Тип повернення вашого Functionмає бути a CompletionStage. Наступний Functionу ланцюжку отримає результат цього CompletionStageвведення, таким чином розгортаючи CompletionStage.

// addOneAsync may be implemented by using another thread, or calling a remote method
abstract CompletableFuture<Integer> addOneAsync(int input);

CompletableFuture.completedFuture(1)
    .thenCompose((x)->addOneAsync(x)) // doing something asynchronous, returns CompletableFuture<Integer>
    .thenApply((y)->System.println(y)); // y is an Integer, the result of CompletableFuture<Integer> above

Це ідея, схожа на Javascript Promise. Promise.thenможе прийняти функцію, яка або повертає значення, або Promiseзначення. Причина, чому ці два способи мають різні назви на Java, пов’язана із загальним стиранням . Function<? super T,? extends U> fnі Function<? super T,? extends CompletionStage<U>> fnвважаються тим самим типом виконання - Function. Таким чином thenApplyі thenComposeповинні бути чітко названі, або компілятор Java скаржиться на однакові підписи методу. Кінцевим результатом є те, що JavaScript Promise.thenреалізований у двох частинах - thenApplyі thenCompose- на Java.

Ви можете прочитати мою іншу відповідь, якщо вас також бентежить пов’язана функція thenApplyAsync.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.