All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.meat.Try Maven / Gradle / Ivy

package io.meat;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * A completed attempt to compute a result that either succeeded or failed.
 *
 * 

Try is especially useful for monadic composition of tasks that may either * succeed or fail, without having to use a series of nested try/catch * statements. You can form a Try using {@link #attempt(Supplier)} or * {@link #attemptApply(Function, Object)}:

* *
{@code
 * Try value = Try.attempt(() -> keyValueStore.fetch("someKey"));
 * Try value = Try.attemptApply(keyValueStore::fetch, "someKey");
 * }
* *

If the function has checked exceptions, you can use * {@link #attemptChecked(CheckedSupplier)} or * {@link #attemptApplyChecked(CheckedFunction, Object)} to wrap any exception * in a RuntimeException.

* *

You can also manually make a successful or failed Try using * {@link #succeed(Object)} and {@link #fail(Throwable)}:

* *
{@code
 * Try value = Try.succeed("someValue");
 * Try failure = Try.fail(new IllegalStateException("Something broke"));
 * }
* *

Once you have a Try, you can transform the value inside it using * {@link #map(Function)}:

* *
{@code
 * Try hex = Try.succeed(123).map(Integer::toHexString);
 * assert hex.equals(Try.succeed("7b"));
 * }
* *

If your function returns a Try, you can use {@link #flatMap(Function)} to * prevent nesting:

* *
{@code
 * Try number = Try.succeed(123);
 * Try nextValue = number.flatMap(num -> {
 *     return Try.succeed("a string");
 * });
 * assert nextValue.equals(Try.succeed("a string"));
 * }
* *

To get values back out of a Try, there are Optionals available as * {@link #getResult()} and {@link #getFailure()} for safe retrieval of either * state:

* *
{@code
 * Try number = Try.succeed(123);
 * assert number.getResult().equals(Optional.of(123));
 * assert !number.getFailure().isPresent();
 * }
* *

If you've checked that a Try is successful, or you don't mind an (unchecked) * exception being raised, use {@link #get()}:

* *
{@code
 * Try number = Try.succeed(123);
 * assert number.get() == 123;
 * }
* *

In the case of a failed Try, get() will wrap the exception in a * RuntimeException; its {@link Throwable#getCause()} will be the * original exception.

* * @param the type of result, if successful. */ public final class Try { private final Result result; private final Throwable failure; private Try(Result result, Throwable failure) { assert (result == null) ^ (failure == null) : "Exactly one of failure or result must be null"; this.result = result; this.failure = failure; } /** * Build a successful Try from a non-null result. */ public static Try succeed(Result result) { if (result == null) { throw new IllegalArgumentException("Try.succeed result may not be null"); } return new Try<>(result, null); } /** * Build a failed Try from an exception or other Throwable. */ public static Try fail(Throwable failure) { if (failure == null) { throw new IllegalArgumentException("Try.fail failure may not be null"); } return new Try<>(null, failure); } /** * Wrap the result of a {@link Supplier} as a Try, catching exceptions. * * @param func a Supplier which returns a Result * @param the type of result returned by func * @return a Try containing either the Supplier's result, or any exception * thrown by {@link Supplier#get()} */ public static Try attempt(Supplier func) { return attemptChecked(func::get); } /** * Similar to {@link #attempt(Supplier)}, but handles checked exceptions. * @param func a CheckedSupplier which returns a Result or throws Exception * @param the type of result returned by func * @return a Try containing either the CheckedSupplier's result, or any * exception thrown by {@link CheckedSupplier#get()} */ public static Try attemptChecked(CheckedSupplier func) { Result result; try { result = func.get(); } catch (Exception e) { return Try.fail(e); } return Try.succeed(result); } /** * Wrap the result of a {@link Function} as a Try, catching exceptions. * * @param func a Function from the input to the result type of the Try * @param input a single argument for func * @param the input type of the function * @param the result type of the function * @return a Try of type Result */ public static Try attemptApply( Function func, Input input) { return attemptApplyChecked(func::apply, input); } /** * Similar to {@link #attemptApply(Function, Object)}, handling checked exceptions. * * @param func a Function from the input to the result type of the Try * @param input a single argument for func * @param the input type of the function * @param the result type of the function * @return a Try of type Result */ public static Try attemptApplyChecked( CheckedFunction func, Input input) { Result result; try { result = func.apply(input); } catch (Exception e) { return Try.fail(e); } return Try.succeed(result); } /** * Get the successful result of this Try, or {@link Optional#empty()} if it failed. * * @return an Optional result */ public Optional getResult() { return Optional.ofNullable(result); } /** * Get the Throwable that caused this Try to fail, or {@link Optional#empty()} if it was successful. * * @return an Optional Throwable */ public Optional getFailure() { return Optional.ofNullable(failure); } /** * Get this Try's result, or throw its failure as an unchecked exception. * * @return the successful result of this Try * @throws RuntimeException wrapping the failure as an unchecked exception */ public Result get() { if (result == null) { throw new RuntimeException(failure); } return result; } /** * Transform the result of this Try. * *

If this Try is successful, the provided function will be applied to * the current result and a new Try of the destination type will be * returned.

*

If this Try is a failure, a new Try of the destination result type * containing the existing failure will be returned.

*

If this Try is successful but the mapping function throws an * exception, a failed Try of the destination result type will be * returned, containing that exception.

* * @param func A function mapping this Try's result type to a new one * @param the output type of the transformation function * @return a new Try of either the transformed result or existing failure */ public Try map(Function func) { if (result == null) { return Try.fail(this.failure); } return Try.attemptApply(func, result); } /** * Transform the result of this Try into a new Try, returning that. * *

The behavior of this function is similar to {@link #map(Function)}, * except that func should return a Try, and this will be returned without * being wrapped further.

*

Any uncaught exceptions thrown by func will themselves be captured in * a Try.

* * @param func a function mapping this Try's result type to another Try * @param the result type of the Try produced by func * @return a Try of the destination result type */ public Try flatMap(Function> func) { if (result == null) { return Try.fail(this.failure); } // This is kind of like Try.attemptApply but we don't wrap the result // of func.apply in Try.succeed. try { return func.apply(result); } catch (Exception e) { return Try.fail(e); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Try aTry = (Try) o; return Objects.equals(result, aTry.result) && Objects.equals(failure, aTry.failure); } @Override public int hashCode() { return Objects.hash(result, failure); } @Override public String toString() { if (result != null) { return "Try{result=" + result + "}"; } else { return "Try{failure=" + failure + "}"; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy