Ce face CompletableFuture.thenApply
thenApply(f) spune: „când rezultatul este gata, aplică-i funcția f”. Returnează imediat un nou CompletableFuture cu rezultatul transformat. Este echivalentul lui map din stream-uri.
thenAccept(actiune) primește rezultatul și face ceva cu el (îl afișează, îl salvează), fără să returneze o valoare nouă. Rezultatul este un CompletableFuture<Void>; îl poți aștepta cu join().
Dacă funcția ta returnează ea însăși un CompletableFuture (de exemplu o a doua cerere care depinde de prima), folosește thenCompose, altfel ajungi la CompletableFuture<CompletableFuture<…>>. Dacă pasul anterior a eșuat, thenApply și thenAccept nu mai rulează: eroarea trece mai departe.
Parametri
| Parametru | Descriere |
|---|---|
functie |
Transformarea aplicată rezultatului, de exemplu s -> s.toUpperCase(). |
actiune |
Ce se face cu rezultatul, de exemplu System.out::println. |
Ce returnează
thenApply: un CompletableFuture<U> cu rezultatul transformat. thenAccept: un CompletableFuture<Void>.
Exemple
Exemplul 1: Transformări în lanț, apoi afișare
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture
.supplyAsync(() -> " ioana ")
.thenApply(String::strip)
.thenApply(nume -> nume.substring(0, 1).toUpperCase() + nume.substring(1))
.thenAccept(nume -> System.out.println("Bun venit, " + nume + "!"))
.join();
}
}
Rezultat
Bun venit, Ioana!
Exemplul 2: thenCompose: o cerere care depinde de alta
Întâi aflăm codul clientului, apoi cu el cerem comenzile. thenCompose „aplatizează” rezultatul.
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class Main {
static CompletableFuture<Integer> codClient(String email) {
return CompletableFuture.supplyAsync(() -> email.equals("ana@exemplu.ro") ? 1042 : -1);
}
static CompletableFuture<List<String>> comenzi(int cod) {
return CompletableFuture.supplyAsync(() -> cod == 1042 ? List.of("Laptop", "Mouse") : List.of());
}
public static void main(String[] args) {
List<String> lista = codClient("ana@exemplu.ro")
.thenCompose(Main::comenzi)
.join();
System.out.println("Comenzile Anei: " + lista);
}
}
Rezultat
Comenzile Anei: [Laptop, Mouse]
Exemplul 3: Pașii nu rulează după o eroare
parseInt eșuează, deci nici thenApply, nici thenAccept nu afișează nimic. handle doar așteaptă finalul fără să arunce excepția.
import java.util.concurrent.CompletableFuture;
public class Main {
public static void main(String[] args) {
CompletableFuture<Void> f = CompletableFuture
.supplyAsync(() -> Integer.parseInt("zece"))
.thenApply(n -> {
System.out.println("thenApply a rulat");
return n * 2;
})
.thenAccept(n -> System.out.println("Rezultat: " + n));
f.handle((rezultat, eroare) -> null).join();
System.out.println("Lanțul s-a terminat cu eroare: " + f.isCompletedExceptionally());
}
}
Rezultat
Lanțul s-a terminat cu eroare: true
Greșeli frecvente
- Folosești
thenApplycu o funcție care returnează unCompletableFuture. Primești un future în future; foloseștethenCompose. - Pui cod lent sau blocant în
thenApply. Rulează pe firul care a completat pasul anterior; pentru lucru greu foloseștethenApplyAsynccu un executor.
Comenzi înrudite
CompletableFuture: Un Future care se poate înlănțui: definești ce se întâmplă cu rezultatul (thenApply, thenAccept) fără să aștepți; join() îl ia la final.CompletableFuture.supplyAsync: Pornește o sarcină pe alt fir și returnează imediat un CompletableFuture; supplyAsync are rezultat, runAsync nu.CompletableFuture.thenCombine: Combină rezultatele a două CompletableFuture independente, după ce sunt gata amândouă.CompletableFuture.exceptionally: Tratează o eroare din lanț: dacă un pas anterior a eșuat, funcția primește excepția și returnează o valoare de rezervă.