Подтвердить что ты не робот

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

Я столкнулся с странным поведением метода 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;
});

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

4b9b3361

Ответ 1

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

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

Не удивительно, если вы считаете, что можете захотеть узнать, на сцене вы вызвали exceptionally on, failed или один из его прямых или косвенных предпосылок.

Ответ 2

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

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;
});