CompletableFuture.thenApply în Java

thenApply transformă rezultatul când este gata (ca map); thenAccept îl folosește fără să returneze ceva (ca forEach).

Sintaxă
<U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> functie)
CompletableFuture<Void> thenAccept(Consumer<? super T> actiune)
<U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> functie)

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 thenApply cu o funcție care returnează un CompletableFuture. Primești un future în future; folosește thenCompose.
  • Pui cod lent sau blocant în thenApply. Rulează pe firul care a completat pasul anterior; pentru lucru greu folosește thenApplyAsync cu 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ă.