CompletableFuture.exceptionally în Java

Tratează o eroare din lanț: dacă un pas anterior a eșuat, funcția primește excepția și returnează o valoare de rezervă.

Sintaxă
CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> functie)
<U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> functie)
CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> actiune)

Ce face CompletableFuture.exceptionally

exceptionally(f) este un catch pentru lanțuri de CompletableFuture. Dacă totul a mers bine, rezultatul trece neschimbat. Dacă un pas anterior a aruncat o excepție, f primește excepția și returnează o valoare de rezervă, iar lanțul continuă normal.

Excepția primită este de obicei învelită într-o CompletionException; cauza reală este în getCause(). handle((rezultat, eroare) -> ...) este apelată mereu, cu una dintre cele două valori null. whenComplete doar observă rezultatul (de exemplu pentru jurnal), fără să-l schimbe.

Parametri

Parametru Descriere
functie Primește excepția (Throwable) și returnează o valoare de același tip ca rezultatul.

Ce returnează

Un nou CompletableFuture<T>: rezultatul original sau valoarea de rezervă.

Exemple

Exemplul 1: Valoare de rezervă când citirea eșuează

import java.util.concurrent.CompletableFuture;

public class Main {
    static CompletableFuture<Integer> varsta(String text) {
        return CompletableFuture
            .supplyAsync(() -> Integer.parseInt(text))
            .exceptionally(e -> {
                System.out.println("Nu pot citi \"" + text + "\": " + e.getCause().getClass().getSimpleName());
                return 0;
            });
    }

    public static void main(String[] args) {
        System.out.println("Vârsta: " + varsta("34").join());
        System.out.println("Vârsta: " + varsta("treizeci").join());
    }
}

Rezultat

Vârsta: 34
Nu pot citi "treizeci": NumberFormatException
Vârsta: 0

Exemplul 2: handle: un singur loc pentru succes și eroare

import java.util.concurrent.CompletableFuture;

public class Main {
    static String imparte(int total, int persoane) {
        return CompletableFuture
            .supplyAsync(() -> total / persoane)
            .handle((parte, eroare) -> eroare == null
                ? "Fiecare plătește " + parte + " lei"
                : "Eroare: " + eroare.getCause().getMessage())
            .join();
    }

    public static void main(String[] args) {
        System.out.println(imparte(300, 4));
        System.out.println(imparte(300, 0));
    }
}

Rezultat

Fiecare plătește 75 lei
Eroare: / by zero

Exemplul 3: Eroarea sare peste pașii intermediari

thenApply nu rulează după eroare; exceptionally de la final o prinde.

import java.util.concurrent.CompletableFuture;

public class Main {
    public static void main(String[] args) {
        String rezultat = CompletableFuture
            .supplyAsync(() -> {
                if (true) throw new IllegalStateException("serverul de plăți nu răspunde");
                return 100;
            })
            .thenApply(suma -> "Plată reușită: " + suma + " lei")
            .exceptionally(e -> "Plată eșuată: " + e.getCause().getMessage())
            .join();
        System.out.println(rezultat);
    }
}

Rezultat

Plată eșuată: serverul de plăți nu răspunde

Greșeli frecvente

  • Afișezi e.getMessage() direct. Excepția este de obicei o CompletionException, iar mesajul conține și numele clasei interne; folosește e.getCause().
  • Pui exceptionally la începutul lanțului și te aștepți să prindă erorile pașilor de după. Prinde doar erorile de dinaintea lui.

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.thenApply: thenApply transformă rezultatul când este gata (ca map); thenAccept îl folosește fără să returneze ceva (ca forEach).
  • Exception.getCause: Returnează excepția originală care a produs-o pe aceasta (lanț de excepții), sau null dacă nu există.
  • CompletableFuture.allOf: Returnează un CompletableFuture care se termină când s-au terminat toate cele primite; anyOf, când s-a terminat primul.