Created
June 23, 2015 04:04
-
-
Save timjstewart/c733fd9d36d9781f258b to your computer and use it in GitHub Desktop.
A Java class similar to Haskell's Either.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.timjstewart; | |
| import java.util.Objects; | |
| import java.util.Optional; | |
| import java.util.function.Consumer; | |
| import java.util.NoSuchElementException; | |
| /** | |
| * A value that can either be a value or a exception. | |
| * | |
| * @param <T> the type of the successful value. | |
| */ | |
| public abstract class Result<T> { | |
| /** | |
| * A function that is not defined for every element in its domain (e.g. a partial function). | |
| * <p> | |
| * <p> | |
| * If the partial function is called with a value that is not in its domain, it throws an exception. | |
| * | |
| * @param <T> the type of the function's parameter. | |
| * @param <U> the type of the function's return value | |
| */ | |
| @FunctionalInterface | |
| public interface PartialFunction<T, U> { | |
| U apply(T object) throws Exception; | |
| } | |
| /** | |
| * Creates a successful {@code Result<T>} | |
| * | |
| * @param success the successful value | |
| * @param <T> the successful value's type | |
| * @return a successful {@code Result<T>} whose value is {@code value}. | |
| */ | |
| public static <T> Result<T> success(final T success) { | |
| return new Success<>(success); | |
| } | |
| /** | |
| * Creates a failed {@code Result<T>} | |
| * | |
| * @param failure the exception (an {@code Exception}) | |
| * @return a failed {@code Result<T>} whose value is an {@code Exception}. | |
| */ | |
| public static <T> Result<T> failure(final Exception failure) { | |
| return new Failure<>(failure); | |
| } | |
| /** | |
| * If this object is of type {@code Success<T>}, this method applies {@code func} to this object's | |
| * value and returns the result wrapped in a {@code Result<T>}. | |
| * <p> | |
| * Otherwise, this method just returns this object so that the original exception is propagated through. | |
| * | |
| * @param func a partial function that can throw an Exception | |
| * @param <U> the return type of {@code func}. | |
| * | |
| * @return a {@code Result} that wrap the result of calling func. | |
| */ | |
| public abstract <U> Result<U> map(PartialFunction<? super T, ? extends U> func); | |
| public abstract <U> Result<U> flatMap(PartialFunction<? super T, Result<U>> func); | |
| public static <U> Result<U> of(final Optional<U> optional) { | |
| Objects.requireNonNull(optional); | |
| if (optional.isPresent()) { | |
| return success(optional.get()); | |
| } else { | |
| return failure(new NoSuchElementException("No value present")); | |
| } | |
| } | |
| /** | |
| * Indicates whether or not this object represents a {@code Success}. | |
| * | |
| * @return true iff this object is a @{code Success}. | |
| */ | |
| public abstract boolean getValue(); | |
| /** | |
| * Indicates whether or not this object represents a {@code Failure}. | |
| * | |
| * @return true iff this object is a @{code Failure}. | |
| */ | |
| public abstract boolean getException(); | |
| /** | |
| * @return this object as a {@code Success<T>} iff if this object really is a {@code Success<T>}. | |
| * | |
| * @throws IllegalStateException if this object is not a {@code Success<T>} | |
| */ | |
| public abstract Success<T> asSuccess(); | |
| /** | |
| * @return this object as a {@code Failure<T>} iff if this object really is a {@code Failure<T>}. | |
| * | |
| * @throws IllegalStateException if this object is not a {@code Failure<T>} | |
| */ | |
| public abstract Failure<T> asFailure(); | |
| public abstract Optional<T> asOptional(); | |
| public abstract T get() throws Exception; | |
| public abstract void ifPresent(Consumer<? super T> consumer); | |
| /** | |
| * Return the value if present, otherwise return {@code other}. | |
| * | |
| * @param other the value to be returned if there is no value present, may | |
| * be null | |
| * @return the value, if present, otherwise {@code other} | |
| */ | |
| public abstract T orElse(T other); | |
| final static class Success<T> extends Result<T> { | |
| private final T value; | |
| public Success(final T value) { | |
| this.value = Objects.requireNonNull(value); | |
| } | |
| @Override final public <U> Result<U> map(final PartialFunction<? super T, ? extends U> func) { | |
| try { | |
| return success(func.apply(value)); | |
| } catch (Exception ex) { | |
| return failure(ex); | |
| } | |
| } | |
| @Override final public <U> Result<U> flatMap(final PartialFunction<? super T, Result<U>> func) { | |
| try { | |
| return func.apply(value); | |
| } catch (Exception ex) { | |
| return failure(ex); | |
| } | |
| } | |
| @Override public final String toString() { return "Success [ value=" + value + "]"; } | |
| @Override public final boolean getValue() { return true; } | |
| @Override public final boolean getException() { return false; } | |
| @Override public final Success<T> asSuccess() { return this; } | |
| @Override public final Failure<T> asFailure() { | |
| throw new IllegalStateException("not a Failure"); | |
| } | |
| @Override public final T get() throws Exception { return value; } | |
| @Override public final Optional<T> asOptional() { return Optional.of(value); } | |
| @Override public final void ifPresent(Consumer<? super T> consumer) { consumer.accept(value); } | |
| /** | |
| * Return the value if present, otherwise return {@code other}. | |
| * | |
| * @param other the value to be returned if there is no value present, may | |
| * be null | |
| * @return the value, if present, otherwise {@code other} | |
| */ | |
| @Override public final T orElse(T other) { | |
| return value; | |
| } | |
| } | |
| final static class Failure<T> extends Result<T> { | |
| private final Exception exception; | |
| public Failure(Exception exception) { | |
| this.exception = Objects.requireNonNull(exception); | |
| } | |
| @Override public final <U> Result<U> map(PartialFunction<? super T, ? extends U> func) { | |
| return failure(exception); | |
| } | |
| @Override public final <U> Result<U> flatMap(PartialFunction<? super T, Result<U>> func) { | |
| return failure(exception); | |
| } | |
| @Override public final String toString() { return "Failure [exception=" + exception + "]"; } | |
| @Override public final boolean getValue() { return false; } | |
| @Override public final boolean getException() { return true; } | |
| @Override public final Success<T> asSuccess() { | |
| throw new IllegalStateException("not a Success"); | |
| } | |
| @Override public final Failure<T> asFailure() { return this; } | |
| @Override public final T get() throws Exception { throw exception; } | |
| @Override public final Optional<T> asOptional() { return Optional.empty(); } | |
| @Override public final void ifPresent(Consumer<? super T> consumer) { } | |
| /** | |
| * Return the value if present, otherwise return {@code other}. | |
| * | |
| * @param other the value to be returned if there is no value present, may | |
| * be null | |
| * @return the value, if present, otherwise {@code other} | |
| */ | |
| @Override public final T orElse(T other) { | |
| return other; | |
| } | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment