Skip to content

Commit 8b0f051

Browse files
authored
Implement recoverAndTry and recoverAllAndTry (#2948)
1 parent b9ec2de commit 8b0f051

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

vavr/src/main/java/io/vavr/control/Try.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,62 @@ default Try<T> recoverWith(Function<? super Throwable, ? extends Try<? extends T
982982
}
983983
}
984984

985+
/**
986+
* Returns {@code this}, if this is a {@link Try.Success}, otherwise attempts to recover from any failure
987+
* by evaluating the given {@code recoveryAttempt} (via {@link Try#of(CheckedFunction0)}).
988+
*
989+
* <pre>{@code
990+
* // = Success(5)
991+
* Try.of(() -> 5)
992+
* .recoverAllAndTry(() -> 10);
993+
*
994+
* // = Success(10)
995+
* Try.of(() -> 1/0)
996+
* .recoverAllAndTry(() -> 10);
997+
* }</pre>
998+
*
999+
* @param recoveryAttempt A checked function that provides a fallback in case of a failure
1000+
* @return a {@code Try} that is either this {@code Success} or a new {@code Try} evaluated from {@code recoveryAttempt}
1001+
* @throws NullPointerException if {@code recoveryAttempt} is null
1002+
*/
1003+
default Try<T> recoverAllAndTry(CheckedFunction0<? extends T> recoveryAttempt) {
1004+
Objects.requireNonNull(recoveryAttempt, "recoveryAttempt is null");
1005+
return isFailure() ? of(recoveryAttempt) : this;
1006+
}
1007+
1008+
/**
1009+
* Returns {@code this}, if this is a {@link Try.Success}, otherwise attempts to recover from failure when the
1010+
* underlying cause is assignable to the specified {@code exceptionType}, by evaluating the given
1011+
* {@code recoveryAttempt} (via {@link Try#of(CheckedFunction0)}).
1012+
*
1013+
* <pre>{@code
1014+
* // = Success(5)
1015+
* Try.of(() -> 5)
1016+
* .recoverAndTry(ArithmeticException.class, () -> 10);
1017+
*
1018+
* // = Success(10)
1019+
* Try.of(() -> 1/0)
1020+
* .recoverAndTry(ArithmeticException.class, () -> 10);
1021+
*
1022+
* // = Failure(java.lang.ArithmeticException: / by zero)
1023+
* Try.of(() -> 1/0)
1024+
* .recoverAndTry(NullPointerException.class, () -> 10);
1025+
* }</pre>
1026+
*
1027+
* @param <X> The type of the exception that may be recovered
1028+
* @param exceptionType The specific exception type that should trigger the recovery
1029+
* @param recoveryAttempt A checked function that provides a fallback in case of a matching failure
1030+
* @return a {@code Try} that is either this {@code Success}, or a new {@code Try} evaluated from {@code recoveryAttempt}
1031+
* @throws NullPointerException if {@code exceptionType} or {@code recoveryAttempt} is null
1032+
*/
1033+
default <X extends Throwable> Try<T> recoverAndTry(Class<X> exceptionType, CheckedFunction0<? extends T> recoveryAttempt) {
1034+
Objects.requireNonNull(exceptionType, "exceptionType is null");
1035+
Objects.requireNonNull(recoveryAttempt, "recoveryAttempt is null");
1036+
return isFailure() && exceptionType.isAssignableFrom(getCause().getClass())
1037+
? of(recoveryAttempt)
1038+
: this;
1039+
}
1040+
9851041
/**
9861042
* Converts this {@code Try} to an {@link Either}.
9871043
*

vavr/src/test/java/io/vavr/control/TryTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,65 @@ public void shouldNotRecoverFailureWhenExceptionTypeIsntAssignable() {
949949
assertThat(Try.of(() -> {throw error;}).recoverWith(Error.class, success()).getCause()).isSameAs(error);
950950
}
951951

952+
// -- recoverAllAndTry
953+
954+
@Test
955+
public void shouldRecoverFailure() {
956+
assertThat(failure()
957+
.recoverAllAndTry(() -> OK))
958+
.isEqualTo(Try.success(OK));
959+
}
960+
961+
@Test
962+
public void shouldNotRecoverSuccess() {
963+
final String initialValue = "INITIAL";
964+
final String attemptValue = "RECOVERY";
965+
assertThat(Try.success(initialValue)
966+
.recoverAllAndTry(() -> attemptValue))
967+
.isEqualTo(Try.success(initialValue));
968+
}
969+
970+
@Test
971+
public void shouldThrowNullPointerExceptionWhenRecoveryAttemptIsNull() {
972+
assertThrows(NullPointerException.class, () -> failure().recoverAllAndTry(null));
973+
}
974+
975+
// -- recoverAndTry
976+
977+
@Test
978+
public void shouldRecoverCorrectTypeOfFailure() {
979+
assertThat(Try.failure(new RuntimeException())
980+
.recoverAndTry(RuntimeException.class, () -> OK))
981+
.isEqualTo(Try.success(OK));
982+
}
983+
984+
@Test
985+
public void shouldNotRecoverIncorrectTypeOfFailure() {
986+
Try<Object> initialFailure = Try.failure(new RuntimeException());
987+
assertThat(initialFailure
988+
.recoverAndTry(IllegalStateException.class, () -> OK))
989+
.isEqualTo(initialFailure);
990+
}
991+
992+
@Test
993+
public void shouldNotRecoverSuccessForRecoverAndTry() {
994+
final String initialValue = "INITIAL";
995+
final String attemptValue = "RECOVERY";
996+
assertThat(Try.success(initialValue)
997+
.recoverAndTry(Throwable.class, () -> attemptValue))
998+
.isEqualTo(Try.success(initialValue));
999+
}
1000+
1001+
@Test
1002+
public void shouldThrowNullPointerExceptionWhenExceptionTypeIsNull() {
1003+
assertThrows(NullPointerException.class, () -> failure().recoverAndTry(null, () -> OK));
1004+
}
1005+
1006+
@Test
1007+
public void shouldThrowNullPointerExceptionWhenRecoveryAttemptIsNullForRecoverAndTry() {
1008+
assertThrows(NullPointerException.class, () -> failure().recoverAndTry(Throwable.class, null));
1009+
}
1010+
9521011
// -- onFailure
9531012

9541013
@Test

0 commit comments

Comments
 (0)