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

com.github.simonharmonicminor.juu.monad.Try Maven / Gradle / Ivy

There is a newer version: 1.2
Show newest version
package com.github.simonharmonicminor.juu.monad;

import com.github.simonharmonicminor.juu.collection.Streaming;
import com.github.simonharmonicminor.juu.lambda.Action;
import com.github.simonharmonicminor.juu.lambda.CheckedFunction;
import com.github.simonharmonicminor.juu.lambda.CheckedSupplier;

import java.util.Arrays;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Monad for retrieving values just like {@link java.util.Optional}, but instead a container
 * considered as an empty one if an exception has been thrown during calculations.
 *
 * 


* Class overrides {@link Object#equals(Object)} and {@link Object#hashCode()} methods. * * @param the type of the return value * @since 1.0 */ public class Try implements Streaming { private static final Try EMPTY = new Try<>(null, true); private final T value; private final boolean exceptionHasBeenThrown; private Try(T value) { this.value = value; this.exceptionHasBeenThrown = false; } private Try(T value, boolean exceptionHasBeenThrown) { this.value = value; this.exceptionHasBeenThrown = exceptionHasBeenThrown; } /** * Instantiates a container by calculating the value of the supplier. If supplier fails with an * exception, returns {@link Try#empty()} * * @param supplier function that returns value * @param the type of the return value * @param the type of the exception that supplier may throw * @return a container with the retrieved value or an empty one, if calculation failed with an * exception * @throws NullPointerException if suppliers parameter is null */ public static Try of(CheckedSupplier supplier) { Objects.requireNonNull(supplier); try { return new Try<>(supplier.get()); } catch (Throwable e) { return empty(); } } /** * Returns a container with the value of the first calculated supplier that didn't fail. If all * suppliers fail, returns {@link Try#empty()} * * @param suppliers collection of suppliers * @param the type of the return value * @param the type of the exception that supplier may throw * @return a container with the value of the succeeded supplier or {@link Try#empty()} * @throws NullPointerException if suppliers parameter is null * @see Try#of(CheckedSupplier) */ public static Try getFirst( Iterable> suppliers) { Objects.requireNonNull(suppliers); for (CheckedSupplier supplier : suppliers) { Try t = Try.of(supplier); if (t.isPresent()) return t; } return empty(); } /** * Proxy method for {@link Try#getFirst(Iterable)} * * @throws NullPointerException if suppliers parameter is null */ @SafeVarargs public static Try getFirst(CheckedSupplier... suppliers) { Objects.requireNonNull(suppliers); return getFirst(Arrays.stream(suppliers).collect(Collectors.toList())); } /** * Returns the empty container. Does not create the new one, returns the same instance every time * * @param the type of the return value * @return empty container */ public static Try empty() { @SuppressWarnings("unchecked") Try t = (Try) EMPTY; return t; } /** * @return true if value is present, otherwise false */ public boolean isPresent() { return !isEmpty(); } /** * @return true if container is empty, otherwise false */ public boolean isEmpty() { return exceptionHasBeenThrown; } /** * Maps the value from one to another and returns new container. If mapper throws an exception, * returns {@link Try#empty()} * * @param mapper mapping function * @param the type of the new value * @param the type of the exception that mapper may throw * @return a container with the new value or an empty one * @throws NullPointerException if mapper is null */ public Try map(CheckedFunction mapper) { Objects.requireNonNull(mapper); return Try.of(() -> mapper.apply(value)); } /** * Maps the value from one to another and returns new container. The mapping function must return * another {@link Try} container. If calculation does not fail, returns the new container, * otherwise returns an empty one.
*
* For instance,
*
* * Try.of(() -> 1)
*    .flatMap(v -> Try.of(() -> v + 1))
*    .orElse(-1) *

*
* returns 2, while
*
* * Try.of(() -> 1)
*    .flatMap(v -> Try.of(() -> v / 0))
*    .orElse(-1) *

*
* returns -1 * * @param mapper mapping function * @param the type of the new value * @param the type of the exception that mapper may throw * @return a container with new value or an empty one * @throws NullPointerException if mapper is null * @see Try#map(CheckedFunction) */ @SuppressWarnings("unchecked") public Try flatMap( CheckedFunction, E> mapper) { Objects.requireNonNull(mapper); try { return (Try) mapper.apply(value); } catch (Throwable e) { return empty(); } } /** * If container is empty or predicate's test returns false, returns {@link Try#empty()}, otherwise * returns the container itself * * @param predicate predicate function * @return the container itself or an empty one * @throws NullPointerException if predicate is null */ public Try filter(Predicate predicate) { Objects.requireNonNull(predicate); if (isEmpty() || !predicate.test(value)) { return empty(); } return this; } /** * @return the value of the container * @throws NoSuchElementException if container is empty */ public T get() { if (isPresent()) return value; throw new NoSuchElementException("Container is empty"); } /** * @param other the default value * @return the value of the container if it is not empty, otherwise returns default value */ public T orElse(T other) { return isPresent() ? value : other; } /** * If container is not empty, returns itself, otherwise returns the new one with the given * supplier * * @param supplier supplier for the new container * @param the type of the exception that container may throw * @return the container itself or the new one * @throws NullPointerException if supplier is null */ public Try orElseTry(CheckedSupplier supplier) { Objects.requireNonNull(supplier); return isPresent() ? this : Try.of(supplier); } /** * If container is not empty, returns its value, otherwise returns the result of the given * supplier * * @param supplier supplier which returns default value * @return the value of the container or the default one * @throws NullPointerException if supplier is null */ public T orElseGet(Supplier supplier) { Objects.requireNonNull(supplier); return isPresent() ? value : supplier.get(); } /** * If container is not empty, returns its value, otherwise throws given exception * * @param exceptionSupplier supplier, that returns exception * @param the type of the exception * @return the value of the container * @throws E if container is empty * @throws NullPointerException if exceptionSupplier is null */ public T orElseThrow(Supplier exceptionSupplier) throws E { Objects.requireNonNull(exceptionSupplier); if (isPresent()) return value; throw exceptionSupplier.get(); } /** * If container is not empty, calls consumer with its value * * @param consumer consumer that will be called, if container is not empty * @throws NullPointerException if consumer is null */ public void ifPresent(Consumer consumer) { Objects.requireNonNull(consumer); if (isPresent()) consumer.accept(value); } /** * If container is empty, executes the action * * @param action action that will be executed, if container is empty * @throws NullPointerException if action is null */ public void ifEmpty(Action action) { Objects.requireNonNull(action); if (isEmpty()) action.execute(); } /** * @return stream of container's value if it is not empty, otherwise {@link Stream#empty()} */ @Override public Stream stream() { return isPresent() ? Stream.of(value) : Stream.empty(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Try aTry = (Try) o; return exceptionHasBeenThrown == aTry.exceptionHasBeenThrown && value.equals(aTry.value); } @Override public int hashCode() { return Objects.hash(value, exceptionHasBeenThrown); } }