io.github.oliviercailloux.jaris.exceptions.Try Maven / Gradle / Ivy
package io.github.oliviercailloux.jaris.exceptions;
/**
* Represents either a result or a failure and provides operations to deal with cases of successes
* and of failures in a unified way.
*
* An instance of this class contains either a (non-{@code null}) result, in which case it is called
* a “success”; or a cause of type {@code X} (some {@link Exception}), in which case it is called a
* “failure”.
*
*
* Instances of this type are immutable.
*
*
* This type provides transformation operations that admit functional operators that can throw
* exceptions. Some of these methods will catch checked exceptions thrown by such functional
* operators, while others will propagate any exception thrown to the caller (see the method
* documentation). When the documentation of a method indicates that it catches checked exceptions
* thrown by some provided functional interface, it is implicit that if the provided functional
* interface throws anything that is not a checked exception, then it is not caught, and simply
* propagated to the caller.
*
*
* Inspired by Vavr. One notable difference is that
* this library does not sneaky throw (see the contract of Vavr’s Try#get()
* and its implementation).
*
*
* @param the type of result possibly kept in the instance.
* @param the type of cause possibly kept in the instance.
*/
public interface Try
extends TryOptionalImpl.TryVariableCatchInterface {
/**
* Returns a success containing the given result.
*
* @param the type of result declared to be (and effectively) kept in the instance
* @param the type of cause declared to be (but not effectively) kept in the instance.
* @param result the result to contain
*/
public static Try success(T result) {
return TryOptionalImpl.TrySuccess.given(result);
}
/**
* Returns a failure containing the given cause.
*
* @param the type of result declared to be (but not effectively) kept in the instance
* @param the type of cause declared to be (and effectively) kept in the instance.
* @param cause the cause to contain
*/
public static Try failure(X cause) {
return TryOptionalImpl.TryFailure.given(cause);
}
/**
* Attempts to get and encapsulate a result from the given supplier.
*
* This method returns a failure iff the given supplier throws a checked exception.
*
* @param the type of result declared to be kept in the instance
* @param the type of cause declared to be kept in the instance; a sort of exception that the
* supplier may throw.
* @param supplier the supplier to get a result from
* @return a success containing the result if the supplier returns a result; a failure containing
* the throwable if the supplier throws a checked exception
* @throws NullPointerException if the supplier returns {@code null}
*/
public static Try
get(Throwing.Supplier extends T, ? extends X> supplier) {
try {
return success(supplier.get());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
/* This is safe, provided the supplier did not sneaky-throw. */
@SuppressWarnings("unchecked")
final X exc = (X) e;
return failure(exc);
}
}
/**
* Runs the runnable iff this instance is a success, and returns this instance if it succeeds and
* the cause of failure if it throws a checked exception; otherwise, returns this instance.
*
* If this instance is a failure, returns this instance without running the provided runnable.
* Otherwise, if the runnable succeeds (that is, does not throw), returns this instance.
* Otherwise, if the runnable throws a checked exception, returns a failure containing the cause
* it threw.
*
* @param runnable the function to run if this instance is a success
* @return a success iff this instance is a success and the provided runnable terminated without
* throwing
*/
@Override
public abstract Try andRun(Throwing.Runnable extends X> runnable);
/**
* Runs the consumer iff this instance is a success, and returns this instance if it succeeds and
* the cause of failure if it throws a checked exception; otherwise, returns this instance.
*
* If this instance is a failure, returns this instance without running the provided consumer.
* Otherwise, if the consumer succeeds (that is, does not throw), returns this instance.
* Otherwise, if the consumer throws a checked exception, returns a failure containing the cause
* it threw.
*
* @param consumer the function to run if this instance is a success
* @return a success iff this instance is a success and the provided consumer terminated without
* throwing
*/
@Override
public abstract Try andConsume(Throwing.Consumer super T, ? extends X> consumer);
/**
* Returns this failure if this instance is a failure; the provided failure if the provided try is
* a failure and this instance is a success; and a success containing the merge of the result
* contained in this instance and the one contained in {@code t2}, if they both are successes and
* {@code merger} does not return {@code null}.
*
* @param the type of result that the provided try is declared to contain
* @param the type of result that the returned try will be declared to contain
* @param a type of exception that the provided merger may throw
* @param t2 the try to consider if this try is a success
* @param merger the function invoked to merge the results if both this and the given try are
* successes
* @return a success if this instance and the given try are two successes
* @throws Y if the merger was applied and threw an exception of type {@code Y}
* @throws NullPointerException if the merger was applied and returned {@code null}
*/
public abstract Try and(Try t2,
Throwing.BiFunction super T, ? super U, ? extends V, Y> merger) throws Y;
/**
* Returns this failure if this instance is a failure; a failure containing the cause thrown by
* the given function if it threw a checked exception; or a success containing the result of
* applying the provided mapper to the result contained in this instance if it is a success and
* the mapper returned a non-{@code null} result.
*
* Equivalent to {@code t.map(r -> Try.get(() -> mapper.apply(r)), c -> t)}.
*
* @param the type of result that the returned try will be declared to contain
* @param mapper the mapper to apply to the result contained in this instance if it is a success
* @return a success iff this instance is a success and the provided mapper returns a
* non-{@code null} value
* @throws NullPointerException if the mapper was applied and returned {@code null}
*/
@Override
public abstract Try
andApply(Throwing.Function super T, ? extends U, ? extends X> mapper);
/**
* Returns this instance if it is a success; otherwise, returns a success if the supplier returns
* a non-{@code null} result.
*
* Returns this instance if it is a success. Otherwise, attempts to get a result from the given
* supplier. If this succeeds, that is, if the supplier returns a non-{@code null} result, returns
* a success containing that result. Otherwise, if the supplier throws a checked exception, this
* method merges both exceptions using the given {@code exceptionMerger} and returns a failure
* containing that merged cause.
*
* @param a type of exception that the provided supplier may throw
* @param the type of cause that the returned try will be declared to contain
* @param a type of exception that the provided merger may throw
* @param supplier the supplier to invoke iff this try is a failure
* @param exceptionsMerger the function invoked to merge both exceptions iff this try is a failure
* and the given supplier threw a checked exception
* @return a success if this instance is a success or the given supplier returned a result
* @throws W if the merger was applied and threw an exception of type {@code W}
* @throws NullPointerException if the supplier or the merger was applied and returned
* {@code null}
*/
public abstract Try or(
Throwing.Supplier extends T, Y> supplier,
Throwing.BiFunction super X, ? super Y, ? extends Z, W> exceptionsMerger) throws W;
}