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 oCompletionException, iar mesajul conține și numele clasei interne; foloseștee.getCause(). - Pui
exceptionallyla î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.