J'ai rencontré un comportement étrange de Java 8 CompletableFuture.exceptionally. Si j'exécute ce code, cela fonctionne très bien et affiche Java.lang.RuntimeException
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
future.exceptionally(e -> {
System.out.println(e.getClass());
return null;
});
Mais si j'ajoute une autre étape dans le futur traitement comme thenApply
, le type d'exception passe à Java.util.concurrent.CompletionException
avec l'exception d'origine enveloppée à l'intérieur.
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
future.thenApply(v-> v).exceptionally(e -> {
System.out.println(e);
return null;
});
Y a-t-il une raison pour que cela se produise? À mon avis, c'est assez surprenant.
Ce comportement est spécifié dans la documentation de classe de CompletionStage
(quatrième puce) :
La méthode
handle
permet en outre à l'étape de calculer un résultat de remplacement qui peut permettre un traitement ultérieur par d'autres étapes dépendantes. Dans tous les autres cas, si le calcul d'une étape se termine brusquement avec une exception ou une erreur (non vérifiée), toutes les étapes dépendantes nécessitant son achèvement se terminent également exceptionnellement, avecCompletionException
contenant l'exception comme sa cause.
Ce n'est pas si surprenant si vous considérez que vous pouvez vouloir savoir si l'étape que vous avez invoquée exceptionally
en cas d'échec, ou l'une de ses prérequis directs ou indirects.
oui, le comportement est attendu, mais si vous voulez l'exception d'origine qui a été levée à partir d'une des étapes précédentes, vous pouvez simplement utiliser ceci
CompletableFuture<String> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException());
future.thenApply(v-> v).exceptionally(e -> {
System.out.println(e.getCause()); // returns a throwable back
return null;
});