Функциональная Java - взаимодействие между ifComplete и исключительно

В этом коде:

doSomethingThatMightThrowAnException()
  .whenComplete((result, ex) -> doSomethingElse()})
  .exceptionally(ex -> handleException(ex));

Если существует исключение из doSomethingThatMightThrowAnException, выполняются как doSomethingElse, так и handleException, или это исключение, потребляемое либо whenComplete, либо exceptionally?

EDIT:

doSomethingThatMightThrowAnException возвращает a CompletableFuture, который может completeExceptionally. Это исключение, о котором я говорю.

Ответ 1

Документация whenComplete гласит:

Возвращает новый CompletionStage с тем же результатом или исключением, что и на этом этапе, который выполняет указанное действие после завершения этого этапа.

(акцент мой)

Это подразумевает, что исключение не поглощается этим этапом, так как предполагается, что оно будет иметь тот же результат или исключение. Однако вы можете быть удивлены тем фактом, что на последующих этапах будет получено исключение из предыдущего этапа, заключенного в исключение CompletionException, как описано здесь, поэтому это не совсем то же исключение:

CompletableFuture<String> test=new CompletableFuture<>();
test.whenComplete((result, ex) -> System.out.println("stage 2: "+result+"\t"+ex))
    .exceptionally(ex -> { System.out.println("stage 3: "+ex); return ""; });
test.completeExceptionally(new IOException());

напечатает:

stage 2: null   java.io.IOException
stage 3: java.util.concurrent.CompletionException: java.io.IOException

Обратите внимание, что вы всегда можете добавить несколько действий на одном этапе вместо цепочки:

CompletableFuture<String> test=new CompletableFuture<>();
test.whenComplete((result, ex) -> System.out.println("stage 2a: "+result+"\t"+ex));
test.exceptionally(ex -> { System.out.println("stage 2b: "+ex); return ""; });
test.completeExceptionally(new IOException());
stage 2b: java.io.IOException
stage 2a: null  java.io.IOException

Конечно, поскольку теперь нет никакой зависимости между 2a и 2b, между ними нет упорядочения, и в случае асинхронного действия они могут выполняться одновременно.

Ответ 2

exceptionally указывает:

Возвращает новый CompletableFuture, который завершается, когда этот CompletableFuture завершается, с результатом данной функции исключения, запускающей это CompletableFuture завершение, когда оно завершается исключительно; в противном случае, если этот CompletainFuture завершается нормально, то возвращаемый CompletableFuture также заканчивается обычно с тем же значением. Примечание. Более гибкие версии этой функциональности доступны с использованием методов whenComplete и handle.

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

Ответ 3

doSomethingThatMightThrowAnException() .whenComplete((result, ex) → doSomethingElse()}) с .whenComplete((result, ex) → doSomethingElse()}) и .exceptionally(ex → handleException(ex)); но если оно выдает исключение, оно заканчивается тут же, так как никакой объект не будет передан в цепочке.

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