io.reacted.patterns.Try Maven / Gradle / Ivy
/*
* Copyright (c) 2020 , [ [email protected] ]
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package io.reacted.patterns;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
public abstract class Try {
public static final Try VOID = Try.of(Try::noOp);
private static final Try.Failure FILTER_FAILED = Try.ofFailure(new NoSuchElementException());
private static final UnsupportedOperationException FAILURE_UNSUPPORTED_OPERATION_EXCEPTION =
new UnsupportedOperationException("Failure has not result value");
private static final UnsupportedOperationException SUCCESS_UNSUPPORTED_OPERATION_EXCEPTION =
new UnsupportedOperationException("Success has no failure cause");
private Try() { /* No object construction allowed */ }
/**
* Placeholder for a no operation
*
* @param args any number or type of args
* @return Void
*/
@SuppressWarnings("SameReturnValue")
public static Void noOp(Object... args) {
return null;
}
/**
* Identity Try function. May be used as placeholder as mapper
* @param value a Try
* @return the same try passed as argument
*/
public static Try identity(Try value) {
return value;
}
/**
* Identity function. May be used as placeholder as mapper
* @param any type
* @return the same value passed as input
*/
public static U identity(U value) {
return value;
}
/**
* @return true if the operation has been successful. False otherwise
*/
public abstract boolean isSuccess();
/**
* @return true if the operation has failed, true otherwise
*/
public boolean isFailure() {
return !isSuccess();
}
/**
* @return Return the cause of a failure
*/
public abstract Throwable getCause();
/**
* This get will return the success value of a successful Try. Be aware that if the success
* value is a NULL,
* a Try.Success object will be returned whose value returned by get() is NULL
* Nulls can safely be used as return values with Try, just remember that you will get what
* you supply
*
* @return get the success value for this Try.
*/
public abstract T get();
/**
* @return An optional containing a non null value on Try success, empty otherwise
*/
public Optional toOptional() {
return isSuccess() ? Optional.ofNullable(get()) : Optional.empty();
}
/**
* Converts the Try into a java Stream. If the Try is successful and
* contains a non-null value,
* a stream containing that values is returned, otherwise an empty stream.
* {@code
* anyCollection.stream()
* .flatMap(element -> Try.of(() -> riskyCall(element)).stream())
* .collect(...)}
*
* @return A stream containing the Try a non-null value on success, empty otherwise
*/
public Stream stream() {
return toOptional().stream();
}
/**
* Applies a consumer on a resource. The resource is automatically closed regardless of the result of the
* computation
*
* {@code
* int stringLengthSum = Try.withResources(() -> Files.lines(path),
* inputStream -> inputStream.mapToInt(String::length)
* .sum())
* .orElse(-1, Throwable::printStackTrace);
* }
*
* @param resourceToResult Mapper function that takes as input the resource and returns a value
* @param resourceSupplier Supplier for the AutoCloseable resource
* @param Mapper return type
* @param Type of the AutoCloseable resource
* @return try of the mapper result
*/
public static
Try withResources(TryResourceSupplier extends A> resourceSupplier,
TryMapper super A, ? extends T> resourceToResult) {
try {
try (A resource = Objects.requireNonNull(resourceSupplier).get()) {
return Try.ofSuccess(Objects.requireNonNull(resourceToResult).apply(resource));
}
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Applies a consumer on two resources. The resources are automatically closed regardless of the result of the
* computation
*
* {@code
* int stringLengthSum = Try.withResources(() -> Files.lines(path),
* () -> Files.lines(anotherPath),
* (firstInputStream, secondInputStream)
* -> Stream.concat(firstInputStream, secondInputStream)
* .mapToInt(String::length).sum())
* .orElse(-1, Throwable::printStackTrace);
* }
*
* @param resourceToResult Mapper function that takes as input two resources and returns a value
* @param resourceSupplier1 Supplier for the first resource argument
* @param resourceSupplier2 Supplier for the second resource argument
* @param Return type of the mapper
* @param Type of the first AutoCloseable resource
* @param Type of the second AutoCloseable resource
* @return a try of the mapper result
*/
public static
Try withResources(TryResourceSupplier extends A> resourceSupplier1,
TryResourceSupplier extends A1> resourceSupplier2,
UnChecked.CheckedBiFunction super A, ? super A1, ? extends T> resourceToResult) {
try {
try (A resource1 = Objects.requireNonNull(resourceSupplier1).get();
A1 resource2 = Objects.requireNonNull(resourceSupplier2).get()) {
return Try.ofSuccess(Objects.requireNonNull(resourceToResult).apply(resource1, resource2));
}
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
public static
Try withChainedResources(TryResourceSupplier extends A> resourceSupplier1,
TryMapper resourceMapper,
UnChecked.CheckedBiFunction super A, ? super A1, ? extends T> resourceToResult) {
try {
try (A resource1 = Objects.requireNonNull(resourceSupplier1.get());
A1 resource2 = Objects.requireNonNull(resourceMapper).apply(resource1)) {
return Try.ofSuccess(Objects.requireNonNull(resourceToResult).apply(resource1, resource2));
}
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Creates a Try monad with the result of a computation
*
* {@code Try.of(() -> anyCheckedOrUncheckedMethodYouWant(anyInput)) }
*
* @param supplier Supplier of the value contained in the Try
* @param type contained in the Try
* @return a Try containing the value generated by the supplier or failure
*/
public static Try of(TryValueSupplier extends T> supplier) {
try {
return Try.ofSuccess(Objects.requireNonNull(supplier).get());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* @param function Callable used as supplier for the value of the Try
* @param Type returned by the callable
* @return a Try containing the value generated by the callable or failure
*/
public static Try ofCallable(UnChecked.CheckedCallable extends T> function) {
try {
return Try.ofSuccess(Objects.requireNonNull(function).call());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* @param function a simple Runnable function
* @return an anonymous Try containing, failed if it's the case, the exception thrown by the
* Runnable
*/
public static Try ofRunnable(UnChecked.CheckedRunnable function) {
try {
Objects.requireNonNull(function).run();
return Try.VOID;
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Creates a successful Try carrying the specified valued
*
* @param value Value that is contained into the Try
* @param type of the value
* @return a successful Try
*/
public static Success ofSuccess(T value) {
return Success.valueOf(value);
}
/**
* Creates a failed Try carrying the specified failure exception
*
* @param throwable failure value for the Try
* @param type of the failed Try
* @return a failed Try containing the specified throwable as cause
*/
public static Failure ofFailure(Throwable throwable) {
return Failure.valueOf(throwable);
}
/**
* Gets the Try value on success, otherwise throws an exception generated by the provided
* throwable mapper
*
* {@code
* var result = Try.of(() -> performComputation())).orElseThrow(ErrorOccurredException::new);
* }
*
* @param throwableSupplier Supplier of the exception that is going to be thrown if the Try
* is a failure
* @param type of the thrown exception
* @return the value contained in the Try if it is successful
* @throws X any exception
*/
@SuppressWarnings("UnusedReturnValue")
public T orElseThrow(Function throwableSupplier) throws X {
if (!isSuccess()) {
throw Objects.requireNonNull(throwableSupplier).apply(getCause());
}
return get();
}
/**
* Gets the Try value on success, otherwise sneaky throws the exception that caused the failure
*
* {@code
* var result = Try.of(() -> performComputation())).orElseSneakyThrow();
* }
*
* @return the value contained in the Try if it is successful
*/
public T orElseSneakyThrow() throws X {
if(isFailure()) {
UnChecked.sneakyThrow(getCause());
}
return get();
}
/**
* Gets the value contained in Try on success, otherwise returns the specified value.
*
* {@code
* var result = Try.of(() -> dangerousComputation()).orElse(anotherObject);
* }
*
* @param alternative An alternative value to return in case the Try is a failure
* @param (sub)type of the alternative value
* @return the original value contained into the Try or the alternative one
*/
public T orElse(U alternative) {
return isSuccess() ? get() : alternative;
}
/**
* Gets the value contained in Try on success, otherwise returns the output of the provided
* supplier.
*
* {@code
* var result = Try.of(() -> dangerousComputation()).orElse(() -> generateAnotherObject());
* }
*
* @param alternative supplier for generating an alternative value in case the Try is a failure
* @return the original value contained into the Try or the one generated by the supplier
*/
public T orElseGet(Supplier extends T> alternative) {
return isSuccess() ? get() : Objects.requireNonNull(alternative).get();
}
public T orElseGet(Supplier extends T> alternative,
Consumer super Throwable> exceptionConsumer) {
if (isFailure()) {
Objects.requireNonNull(exceptionConsumer).accept(getCause());
}
return isSuccess() ? get() : Objects.requireNonNull(alternative).get();
}
public T orElseGet(Function super Throwable, ? extends T> exceptionMapper) {
return isSuccess() ? get() : exceptionMapper.apply(getCause());
}
/**
* Gets the value contained in Try on success, otherwise returns the specified value and the exception that
* generated the failure is processed with the provided consumer
*
* {@code
* var result = Try.of(() -> dangerousComputation()).orElse(anotherObject, LOGGER::print);
* }
*
* @param alternative an alternative Try that should be returned in case the main one is a failure
* @param exceptionConsumer on failure, apply this consumer to the generated Throwable before returning
* the alternative value
* @return the original Try or the alternative one
*/
public T orElse(T alternative, Consumer super Throwable> exceptionConsumer) {
if (isFailure()) {
Objects.requireNonNull(exceptionConsumer).accept(getCause());
}
return isSuccess() ? get() : alternative;
}
/**
* Gets the value contained in Try on success, otherwise maps the exception into a valid return value
*
* {@code
* var result = Try.of(() -> dangerousComputation()).orElseRecover(exception -> exception.toString());
* }
*
* @param exceptionMapper a function that should not throw any exception that maps the throwable to a valid T value
* @return the original Try or the mapped one
*/
public T orElseRecover(Function super Throwable, ? extends T> exceptionMapper) {
return isSuccess() ? get() : Objects.requireNonNull(exceptionMapper).apply(getCause());
}
/**
* Gets the value contained in Try on success, otherwise returns the specified Try
*
* {@code
* var result = Try.of(() -> dangerousComputation()).orElse(anotherTryMonad);
* }
*
* @param alternative an alternative Try that should be returned in case the main one is a failure
* @return the original Try or the alternative one
*/
@SuppressWarnings("unchecked")
public Try orElseTry(Try extends T> alternative) {
return isSuccess() ? this : (Try) Objects.requireNonNull(alternative);
}
/**
* Gets the current Try or if it is a failure, the result of the provided TrySupplier
*
* {@code
* var result = Try.of(() -> dangerousCall()).orElseTry(() -> anotherDangerousCall())
* }
*
* @param supplier supplier for an alternative value in case the main Try is a failure
* @return the original Try or the alternative one
*/
public Try orElseTry(TryValueSupplier extends T> supplier) {
if (isSuccess()) {
return this;
}
try {
return Try.ofSuccess(Objects.requireNonNull(supplier).get());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Gets the current Try or if it is a failure, the result of the provided TrySupplier and the specified consumer
* process the exception that has generated the failure
*
* {@code
* var result = Try.of(() -> dangerousCall()).orElseTry(() -> otherDangerousCall(), LOGGER::printFirstCallExc)
* }
*
* @param supplier supplier for an alternative value in case the main Try is a failure
* @param exceptionConsumer on failure, apply this consumer to the generated Throwable before calling the
* alternative value supplier
* @return the original Try or the alternative one
*/
public Try orElseTry(TryValueSupplier extends T> supplier,
UnChecked.CheckedConsumer super Throwable> exceptionConsumer) {
if (isSuccess()) {
return this;
}
try {
exceptionConsumer.accept(getCause());
return Try.ofSuccess(Objects.requireNonNull(supplier).get());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Gets the current Try or if it is a failure maps the generated exception into an
* alternative Try using
* the specified mapper
*
* {@code
* var result = Try.of(() -> dangerousCall()).orElseTry(throwable -> throwableToAnotherTry(throwable))
* }
*
* @param excToAlternative if the main Try is a failure, the generated exception is taken as
* argument by this mapper for providing an alternate Try
* @return the original value or the generated one if failure
*/
@SuppressWarnings("unchecked")
public Try orElseTry(TryMapper> excToAlternative) {
if (isSuccess()) {
return this;
}
try {
return (Try)excToAlternative.apply(getCause());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Like Stream.flatMap(), Try.flatMap flattens two nested Try. If the Try is successful the flatMapping
* function will be applied, otherwise the current Try will be returned
*
* {@code
* Try result = Try.of(() -> dangerousCall())
* .flatMap(dangerousResult -> Try.of(() -> anotherDangerousCall(dangerousResult)))
* .filter(secondCallResult -> !secondCallResult().isEmpty());
* }
*
* @param flatMapper mapper that transforms the value of a successful Try into
* another Try
* @param type of the argument of the returned Try
* @return a failure or the flatmapped Try
*/
@SuppressWarnings("unchecked")
public Try flatMap(TryMapper super T, Try extends U>> flatMapper) {
Try> unflatMap = map(flatMapper);
return unflatMap.isSuccess() ? (Try) unflatMap.get() : (Failure) unflatMap;
}
/**
* If the Try is successful, another Try will be returned with the
* result of the specified mapper,
* otherwise the current Try is returned
*
* {@code
* Try failedMap = Try.of(TryTests::failingCheckedThrower).map(String::toLowerCase);
* String result = failedMap.orElse(FAILURE);
* }
*
* @param mapper mapper function for transforming a Try to another
* @param type contained into the returned Try
* @return the new Try on success or failure
*/
public Try map(TryMapper super T, ? extends U> mapper) {
if (isSuccess()) {
try {
return Try.ofSuccess(Objects.requireNonNull(mapper).apply(get()));
} catch (Throwable error) {
return Try.ofFailure(error);
}
} else {
return (Failure)this;
}
}
/**
* If the Try is a failure, the current Try is returned.
* If the try Is a success, its value is tested with the specified predicate.
* If such test is successful, the current Try is returned, otherwise a Failure is
* returned
*
* {@code
* String resultString = Try.of(() -> dangerousCallThatReturnsAString())
* .filter(String::isUpperCase)
* .orElse("DEFAULT_UPPERCASE_VALUE");
* }
*
* @param predicate filter predicate for filtering a successful Try. Is a success try
* does not pass the filter, a failed Try will be returned
* @return a Try for which the predicate is true or failure
*/
@SuppressWarnings("unchecked")
public Try filter(UnChecked.CheckedPredicate super T> predicate) {
try {
if (isFailure() ||
isSuccess() && predicate.test(get())) {
return this;
}
return (Try)FILTER_FAILED;
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* If the Try is a failure, the current Try is returned.
* If the try Is a success, its value is tested with the specified predicate.
* If such test is successful, the current Try is returned, otherwise a Failure is
* returned
*
* {@code
* String resultString = Try.of(() -> dangerousCallThatReturnsAString())
* .filter(String::isUpperCase)
* .orElse("DEFAULT_UPPERCASE_VALUE");
* }
*
* @param predicate filter predicate for filtering a successful Try. Is a succeeded try
* does not pass the filter, a failed Try will be returned
* @param exceptionOnTestFailure if the predicate evaluates to false, the failure cause will be provided by this
* @return a Try for which the predicate is true or failure
*/
public Try filter(UnChecked.CheckedPredicate super T> predicate,
Supplier extends Throwable> exceptionOnTestFailure) {
try {
if (isFailure() ||
isSuccess() && predicate.test(get())) {
return this;
}
return Try.ofFailure(exceptionOnTestFailure.get());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* If the Try is a success, the provided consumer is applied to the result. A io.reacted
* .patterns.Try is returned for checking
* the success or the failure of the consumer
*
* {@code
* Try consumerResult = Try.ofSuccess(SUCCESS).ifSuccess(LOGGER::printSuccess);
* }
*
* @param consumer Consumer for the value of a successful Try
* @return an anonymous Try for checking if the consumer execution was successful
*/
public Try ifSuccess(TryConsumer super T> consumer) {
return ifSuccessOrElse(consumer, Try::noOp);
}
/**
* If Try is a failure, the provided consumer will be applied to the generated Throwable.
* A Try is returned for checking the success or the failure of the consumer
*
* {@code
* Try consumerResult = Try.ofFailure(AnyThrowable).ifError(LOGGER::printError);
* }
*
* @param consumer Consumer for the exception contained in a failed Try
* @return an anonymous Try for checking if the consumer execution was successful
*/
public Try ifError(TryConsumer super Throwable> consumer) {
return ifSuccessOrElse(Try::noOp, consumer);
}
/**
* Recover the status of a Try, if failed. If the Try is failed because of an exception of the specified class,
* a new Try will be returned using the provided supplier. If the exception class does not match, the original
* failed Try is returned.
* Different recover calls can be chains for fine exception handling
*
* {@code
* Try catchSpecificException = Try.of(() -> riskyCallReturningString())
* .recover(StringIndexOutOfBoundException.class,
* () -> Try.ofSuccess("Whooops"))
* }
*
* @param exception Exception class we want to intercept
* @param successValGenerator Supplier for an alternate Try in case the provided exception
* class is intercepted
* @param Exception type we want to intercept
* @return a successful Try, the generated one in case the provided exception class is
* intercepted or failure
*/
public Try recover(Class exception,
TryValueSupplier extends T> successValGenerator) {
if (isSuccess() ||
!Objects.requireNonNull(exception).isAssignableFrom(getCause().getClass())) {
return this;
}
try {
return Try.ofSuccess(Objects.requireNonNull(successValGenerator).get());
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Recover the status of a Try, if failed. If the Try is failed because
* of an exception of the specified class,
* the provided Try will be returned. If the exception class does not match, the original
* failed Try is returned
* Different recover calls can be chains for fine exception handling
*
* {@code
* Try catchSpecificException = Try.of(() -> dangerousCallReturningString())
* .recover(StringIndexOutOfBoundException.class, Try.ofSuccess("Whooops"))
* }
*
* @param exception Exception class we want to intercept
* @param successValue alternate Try to be returned in case the provided exception class is
* intercepted
* @param Exception type we want to intercept
* @return a successful Try, the generated one in case the provided exception class is
* intercepted or failure
*/
@SuppressWarnings("unchecked")
public Try recover(Class exception, Try extends T> successValue) {
if (isSuccess() ||
!Objects.requireNonNull(exception).isAssignableFrom(getCause().getClass())) {
return this;
}
return (Try)successValue;
}
/**
* Recover the status of a Try, if failed, a success Try is generated using the provided supplier
* Will be returned the original Try if it is successful, otherwise the try returned by the
* mapper
* Different recover calls can be chains for fine exception handling
*
* {@code
* Try catchSpecificException = Try.of(() -> dangerousCallReturningString())
* .recover(StringIndexOutOfBoundException.class,
* () -> Try.ofSuccess("Whooops"))
* }
*
* @param exception Exception class we want to intercept
* @param successProvider Supplier for the successful try
* @param Exception type we want to intercept
* @return a Try representing the result of the alternative mapping process or the original
* Try
*/
@SuppressWarnings("unchecked")
public Try recover(Class exception,
UnChecked.CheckedSupplier> successProvider) {
if (isSuccess() ||
!Objects.requireNonNull(exception).isAssignableFrom(getCause().getClass())) {
return this;
}
try {
return (Try)Objects.requireNonNull(successProvider).get();
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Recover the status of a Try, if failed, mapping the generated exception to another org
* .reacted.patterns.Try
* Will be returned the original Try if it is successful, otherwise the try returned by the
* mapper
* Different recover calls can be chains for fine exception handling
*
* {@code
* Try catchSpecificException = Try.of(() -> dangerousCallReturningString())
* .recover(StringIndexOutOfBoundException.class,
* exception -> Try.ofSuccess("Whooops: " + exception))
* }
*
* @param exception Exception class we want to intercept
* @param alternativeMapper Mapper that transforms an exception value to a valid result
* @param Exception type we want to intercept
* @return a Try representing the result of the alternative mapping process or the original
* Try
*/
public Try recover(Class exception,
TryMapper super Throwable, ? extends T> alternativeMapper) {
try {
if (isSuccess() ||
!Objects.requireNonNull(exception).isAssignableFrom(getCause().getClass())) {
return this;
}
return Try.ofSuccess(Objects.requireNonNull(alternativeMapper).apply(getCause()));
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* Check the status of the current Try and if it is a success apply the specified consumer to
* its value.
* The Try is left untouched and is returned
*
* {@code
* Try.of(() -> anyCheckedOrUncheckedMethodYouWant(line))
* .peekSuccess(LOGGER::printDebugDiagnostics)
* }
*
* @param ifSuccess consumer for the Try value in case the Try is a success
* @return the untouched Try
*/
public Try peekSuccess(Consumer super T> ifSuccess) {
return peek(ifSuccess, Try::noOp);
}
/**
* Check if the current Try is a Failure and in that case apply the specified consumer to its
* failure cause. The Try is left untouched and is returned
*
* {@code
* Try.of(() -> anyCheckedOrUncheckedMethodYouWant(line))
* .peekFailure(LOGGER::printDebugDiagnostics)
* }
*
* @param ifError if Try is a failure, consumer will be applied to the contained exception
* @return the untouched Try
*/
public Try peekFailure(Consumer super Throwable> ifError) {
return peek(Try::noOp, ifError);
}
/**
* Check if the status of the current Try is a failure of the specified class and in that
* case apply
* the specified consumer to its failure cause. The Try is left untouched and is returned
*
* {@code
* Try.of(() -> methodThrowingAnException(line))
* .peekFailure(ExceptionClassYouWantToIntercept.class, LOGGER::printDebugDiagnostics)
* }
*
* @param ifError if Try is a failure, consumer will be applied to the contained exception
* @return the untouched Try
*/
@SuppressWarnings("UnusedReturnValue")
public Try peekFailure(Class throwableClass,
Consumer ifError) {
return peek(throwableClass, Try::noOp, ifError);
}
/**
* Check the status of the current Try and apply accordingly one of the provided consumers.
* The Try is left untouched and is returned
*
* {@code
* Try.of(() -> anyCheckedOrUncheckedMethodYouWant(line))
* .peek(LOGGER::print, LOGGER::debug)
* }
*
* @param ifSuccess consumer for the Try value in case the Try is a success
* @param ifError consumer for the Throwable in case Try is a failure
* @return the untouched Try
*/
public Try peek(Consumer super T> ifSuccess, Consumer super Throwable> ifError) {
return peek(Throwable.class, ifSuccess, ifError);
}
/**
* Check the status of the current Try and apply accordingly one of the provided consumers.
* The ifError consumer is applied only if this Try is an error and the cause is of the
* specified class.
* The Try is left untouched and is returned
*
* {@code
* Try.of(() -> anyCheckedOrUncheckedMethodYouWant(line))
* .peek(SpecificException.class, LOGGER::print, LOGGER::debug)
* }
*
* @param throwableClass specific throwable class we want to intercept for applying ifError
* @param ifSuccess consumer for the Try value in case the Try is a
* success
* @param ifError consumer for the Throwable in case Try is a failure
* @return the untouched Try
*/
@SuppressWarnings("unchecked")
public Try peek(Class extends ExceptionT> throwableClass,
Consumer super T> ifSuccess,
Consumer super ExceptionT> ifError) {
if (isSuccess()) {
Objects.requireNonNull(ifSuccess).accept(get());
} else if (Objects.requireNonNull(throwableClass).isAssignableFrom(getCause().getClass())) {
Objects.requireNonNull(ifError).accept((ExceptionT)getCause());
}
return this;
}
/**
* A functional if then else: if the Try is successful the ifSuccess mapper is used,
* otherwise the
* throwable mapper.
*
* {@code
* Try.of(() -> riskyCall())
* .mapOrElse(processOutput, exception -> Try.of(() -> tryToRecover()).orElse(PERMANENT_FAILURE))
* .orElse(DEFAULT_RESULT)
* }
*
* @param ifSuccess mapper in case the Try is a success
* @param orElse mapper in case the Try is a failure
* @param the type of the value of the resulting Try
* @return a Try mapped accordingly with the status of the base object
*/
public Try mapOrElse(TryMapper super T, ? extends U> ifSuccess,
TryMapper super Throwable, ? extends U> orElse) {
try {
if(isSuccess()) {
return Try.ofSuccess(Objects.requireNonNull(ifSuccess).apply(get()));
}
return Try.ofSuccess(Objects.requireNonNull(orElse).apply(getCause()));
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
/**
* A functional if then else: if the Try is successful the successConsumer is used, otherwise
* the
* throwable consumer.
*
* {@code
* Try.of(() -> riskyCall())
* .ifSuccessOrElse(processOutput, LOGGER::warn)
* }
*
* @param successConsumer consumer in case the Try is a success
* @param failureConsumer consumer in case the Try is a failure
* @return a Try representing the result of the applied consumer
*/
public Try ifSuccessOrElse(TryConsumer super T> successConsumer,
TryConsumer super Throwable> failureConsumer) {
try {
if (isSuccess()) {
Objects.requireNonNull(successConsumer).accept(get());
} else {
Objects.requireNonNull(failureConsumer).accept(getCause());
}
return Try.VOID;
} catch (Throwable error) {
return Try.ofFailure(error);
}
}
public interface TryMapper extends UnChecked.CheckedFunction {
@Override
R apply(T a) throws Throwable;
}
public interface TryResourceSupplier extends UnChecked.CheckedSupplier {
@Override
T get() throws Throwable;
}
public interface TryValueSupplier extends UnChecked.CheckedSupplier {
@Override
T get() throws Throwable;
}
public interface TryConsumer extends UnChecked.CheckedConsumer {
@Override
void accept(T argument) throws Throwable;
}
public static final class Success extends Try {
private final T successValue;
private Success() {
/* Never Here */
throw new IllegalStateException("Illegal " + this.getClass().getSimpleName() + " Object State");
}
private Success(T successValue) {
this.successValue = successValue;
}
public static Success valueOf(T successValue) {
return new Success<>(successValue);
}
/**
* A successful Try holds a value that can be retrieved with this method. Please note that
* if the success value is NULL, you will get a Try.Success whose value returned by this
* get() is NULL
* If it's the case, please consider to use the toOptional() and stream() methods for safely process
* successful non-null result values
*
* @return returns the value contained in the Try
*/
@Override
public T get() {
return successValue;
}
@Override
public boolean isSuccess() {
return true;
}
/**
* A successful Try does not have any cause
*
* @return None
* @throws UnsupportedOperationException This operation is not allowed on Success
*/
@Override
public Throwable getCause() {
throw SUCCESS_UNSUPPORTED_OPERATION_EXCEPTION;
}
}
public static final class Failure extends Try {
private final Throwable failureValue;
private Failure() {
/* Never Here */
throw new IllegalStateException("Illegal " + this.getClass().getSimpleName() + " Object State");
}
private Failure(Throwable failureValue) {
this.failureValue = failureValue;
}
public static Failure valueOf(Throwable failureValue) {
return new Failure<>(Objects.requireNonNull(failureValue));
}
/**
* A failed Try does not hold any value
*
* @return Nothing
* @throws UnsupportedOperationException This operation is not allowed on a Failure
*/
@Override
public T get() {
throw FAILURE_UNSUPPORTED_OPERATION_EXCEPTION;
}
@Override
public boolean isSuccess() {
return false;
}
@Override
public Throwable getCause() {
return failureValue;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy