Удивительное поведение Java 8 CompletableFuture исключительно методом

Я столкнулся со странным поведением метода Java 8 CompletableFuture.exceptionally. Если я выполняю этот код, он отлично работает и печатает java.lang.RuntimeException

CompletableFuture<String> future = new CompletableFuture<>();

future.completeExceptionally(new RuntimeException());

future.exceptionally(e -> {
            System.out.println(e.getClass());
            return null;
});

Но если я добавлю еще один шаг в будущую обработку, например thenApply, тип исключения изменится на java.util.concurrent.CompletionException с исходным исключением, завернутым внутрь.

CompletableFuture<String> future = new CompletableFuture<>();

future.completeExceptionally(new RuntimeException());

future.thenApply(v-> v).exceptionally(e -> {
            System.out.println(e);
            return null;
});

Есть ли причина, по которой это должно происходить? На мой взгляд, это довольно удивительно.


person Lukas    schedule 11.12.2014    source источник
comment
См. stackoverflow.com/q/49230980/14731 для обзора того, когда исключение упаковано (или нет).   -  person Gili    schedule 06.06.2018
comment
Вот статья, которую я нашел, которая помогла мне лучше понять решения этой проблемы -› millross-consultants.com/   -  person The 0bserver    schedule 12.02.2019
comment
@ The0bserver, ссылка, которую вы добавили, довольно информативна и устраняет многие мои сомнения, хотя и не все :)   -  person NIGAGA    schedule 22.08.2019
comment
Рад, что смог чем-то помочь @NIGAGA.   -  person The 0bserver    schedule 27.08.2019


Ответы (2)


Это поведение описано в документации класса CompletionStage ( четвертый пункт):

Метод handle дополнительно позволяет этапу вычислить результат замены, который может позволить дальнейшую обработку другими зависимыми этапами. Во всех остальных случаях, если вычисление этапа внезапно завершается с (непроверенным) исключением или ошибкой, то все зависимые этапы, требующие его завершения, также завершаются в исключительном порядке с CompletionException с указанием причины исключения.

Это не так уж удивительно, если учесть, что вы можете захотеть узнать, не завершилась ли ошибка этапом, на котором вы вызвали exceptionally, или одним из его прямых или косвенных предварительных условий.

person Holger    schedule 11.12.2014

да, такое поведение ожидаемо, но если вам нужно исходное исключение, которое было выброшено на одном из предыдущих этапов, вы можете просто использовать это

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;
});
person Gagandeep Kalra    schedule 17.06.2017