io.strati.functional.Optional Maven / Gradle / Ivy
Show all versions of strati-functional Show documentation
/*
* Copyright 2016 WalmartLabs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.strati.functional;
import java.util.NoSuchElementException;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* A container object which may or may not contain a non-null value.
* Instances of {@code Optional} are either {@code Present} or {@code Empty}.
* This is an extended implementation of {@code java.util.Optional} that allows
* better composition and removes the need of calling {@code get()} and performing
* imperative checks/actions in most cases.
*/
public abstract class Optional {
/**
* Common instance for {@code empty()}.
*/
private static final Optional> EMPTY = new Empty<>();
/**
* Convert a {java.util.Optional} into a {io.strati.functional.Optional}.
*
* @param jdkOptional the {@code java.util.Optional} to convert
* @param Type of the value
* @return The converted {java.util.Optional} as a {io.strati.functional.Optional}
*/
public static Optional from(final java.util.Optional jdkOptional) {
return jdkOptional.isPresent() ? ofNullable(jdkOptional.get()) : empty();
}
/**
* Returns an empty {@code Optional} instance. No value is present for this
* Optional.
*
* @param Type of the non-existent value
* @return an empty {@code Optional}
*/
public static Optional empty() {
return (Optional) EMPTY;
}
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*/
public static Optional of(final T value) {
return new Present<>(value);
}
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static Optional ofNullable(final T value) {
return value == null ? empty() : of(value);
}
/**
* If a value is present in this {@code Optional}, returns the value,
* otherwise throws {@code NoSuchElementException}.
*
* Note: Try to avoid calling this method as much as possible!
*
* @return the non-null value held by this {@code Optional}
* @throws NoSuchElementException if there is no value present
* @see Optional#isPresent()
*/
public abstract T get();
/**
* Return {@code true} if there is a value present, otherwise {@code false}.
*
* @return {@code true} if there is a value present, otherwise {@code false}
*/
public abstract boolean isPresent();
/**
* Return {@code true} if there is no value present, otherwise {@code false}.
*
* @return {@code true} if there is no value present, otherwise {@code false}
*/
public abstract boolean isEmpty();
/**
* If a value is present, invoke the specified consumer with the value,
* otherwise do nothing.
*
* Note: the (backwards compatible) difference with {@code java.util.Optional}
* here is that {@code ifPresent} returns the current {@code Optional} instead
* of {@code void}, this is so that expressions don't have to be broken up
* in order to do side-effects (i.e. perform actions).
*
* @param consumer block to be executed if a value is present
* @return This instance of {@code Optional} will be returned, no mutations will
* take place.
* @throws NullPointerException if value is present and {@code consumer} is
* null
*/
public abstract Optional ifPresent(final Consumer super T> consumer);
/**
* If no value is present, invoke the specified runnable, otherwise do nothing.
*
* @param runnable block to be executed if no value is present
* @return This instance of {@code Optional} will be returned, no mutations will
* take place.
* @throws NullPointerException if no value is present and {@code runnable} is
* null
*/
public abstract Optional ifEmpty(final Runnable runnable);
/**
* If a value is present, and the value matches the given predicate,
* return an {@code Optional} describing the value, otherwise return an
* empty {@code Optional}.
*
* @param predicate a predicate to apply to the value, if present
* @return an {@code Optional} describing the value of this {@code Optional}
* if a value is present and the value matches the given predicate,
* otherwise an empty {@code Optional}
* @throws NullPointerException if a value is present and the predicate is null
*/
public abstract Optional filter(final Predicate super T> predicate);
/**
* If a value is present, apply the provided mapping function to it,
* and if the result is non-null, return an {@code Optional} describing the
* result. Otherwise return an empty {@code Optional}.
*
* @param The type of the result of the mapping function
* @param mapper a mapping function to apply to the value, if present
* @return an {@code Optional} describing the result of applying a mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if a value is present and the mapping
* function is null
*/
public abstract Optional map(final Function super T, ? extends U> mapper);
/**
* If a value is present, apply the provided {@code Optional}-bearing
* mapping function to it, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*
* @param The type parameter to the {@code Optional} returned by
* @param mapper a mapping function to apply to the value, if present
* the mapping function
* @return the result of applying an {@code Optional}-bearing mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if a value is present and the mapping
* function is null
*/
public abstract Optional flatMap(final Function super T, ? extends Optional extends U>> mapper);
/**
* If no value is present, run the provided supplier function to it,
* return an {@code Optional} describing the result.
* Otherwise return this {@code Optional} without change.
*
* Note: {@code orElseMap} is like {@code map} but for the empty case.
*
* @param mapper
* @return an {@code Optional} describing the result of running a mapping
* supplier, if a value is present and non-null, otherwise an
* empty {@code Optional}
* @throws NullPointerException if no value is present and the mapping
* function is null
*/
public abstract Optional orElseMap(final Supplier extends T> mapper);
/**
* If no value is present, run the provided {@code Optional}-bearing
* mapping supplier, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #orElseMap(Supplier)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code orElseFlatMap} does not wrap it with an additional
* {@code Optional}.
*
* Note: {@code orElseFlatMap} is like {@code flatMap} but for the empty case.
*
* @param mapper a mapping supplier to run if no value is present
* @return the result of running an {@code Optional}-bearing mapping
* supplier, if no value is present, otherwise an empty {@code Optional}
* @throws NullPointerException if no value is present and the mapping
* function is null
*/
public abstract Optional orElseFlatMap(final Supplier extends Optional extends T>> mapper);
/**
* 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(final T other);
/**
* Return the value if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no value
* is present
* @return the value if present otherwise the result of {@code other.get()}
* @throws NullPointerException if value is not present and {@code other} is
* null
*/
public abstract T orElseGet(final Supplier extends T> other);
/**
* Return the contained value, if present, otherwise throw an exception
* to be created by the provided supplier.
*
* @param Type of the exception to be thrown
* @param exceptionSupplier The supplier which will return the exception to
* be thrown
* @return the present value
* @throws X if there is no value present
* @throws NullPointerException if no value is present and
* {@code exceptionSupplier} is null
*/
public abstract T orElseThrow(final Supplier extends X> exceptionSupplier) throws X;
/**
* @return If a value is present return a singleton {@code Stream} containing
* the value, otherwise an empty {@code Stream}.
*/
public abstract Stream stream();
/**
* @return If a value is present returns a {@code Success} containing the value,
* otherwise a {@code Failure} containing a {@code NoSuchElementException}
*/
public abstract Try toTry();
/**
* @return Convert this {@code io.strati.functional.Optional} into a {@code java.util.Optional}
*/
public abstract java.util.Optional toJdkOptional();
}
/**
* The Present class is used to create an object bound, not null value holder.
* Used to guarantee the presence of a value. But should not be used to validate
* the state or internal representation of the value.
*
* @param
*/
final class Present extends Optional {
private final T value;
protected Present(final T value) {
if (value == null) {
throw new NullPointerException();
}
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public boolean isPresent() {
return true;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Optional ifPresent(final Consumer super T> consumer) {
consumer.accept(value);
return this;
}
@Override
public Optional ifEmpty(final Runnable runnable) {
return this;
}
@Override
public Optional filter(final Predicate super T> predicate) {
return predicate.test(value) ? of(value) : empty();
}
@Override
public Optional map(final Function super T, ? extends U> mapper) {
return ofNullable(mapper.apply(value));
}
@Override
public Optional flatMap(final Function super T, ? extends Optional extends U>> mapper) {
final Optional result = (Optional) mapper.apply(value);
return result == null ? empty() : result;
}
@Override
public Optional orElseMap(final Supplier extends T> mapper) {
return this;
}
@Override
public Optional orElseFlatMap(final Supplier extends Optional extends T>> mapper) {
return this;
}
@Override
public T orElse(final T other) {
return value;
}
@Override
public T orElseGet(final Supplier extends T> other) {
return value;
}
@Override
public T orElseThrow(final Supplier extends X> exceptionSupplier) throws X {
return value;
}
@Override
public Stream stream() {
return Stream.of(value);
}
@Override
public Try toTry() {
return Try.success(value);
}
@Override
public java.util.Optional toJdkOptional() {
return java.util.Optional.of(value);
}
}
final class Empty extends Optional {
@Override
public T get() {
throw new NoSuchElementException();
}
@Override
public boolean isPresent() {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Optional ifPresent(final Consumer super T> consumer) {
return this;
}
@Override
public Optional ifEmpty(final Runnable runnable) {
runnable.run();
return this;
}
@Override
public Optional filter(final Predicate super T> predicate) {
return this;
}
@Override
public Optional map(final Function super T, ? extends U> mapper) {
return (Optional) this;
}
@Override
public Optional flatMap(final Function super T, ? extends Optional extends U>> mapper) {
return (Optional) this;
}
@Override
public Optional orElseMap(final Supplier extends T> mapper) {
return ofNullable(mapper.get());
}
@Override
public Optional orElseFlatMap(final Supplier extends Optional extends T>> mapper) {
return (Optional) mapper.get();
}
@Override
public T orElse(final T other) {
return other;
}
@Override
public T orElseGet(final Supplier extends T> other) {
return other.get();
}
@Override
public T orElseThrow(final Supplier extends X> exceptionSupplier) throws X {
throw exceptionSupplier.get();
}
@Override
public Stream stream() {
return Stream.empty();
}
@Override
public Try toTry() {
return Try.failure(new NoSuchElementException());
}
@Override
public java.util.Optional toJdkOptional() {
return java.util.Optional.empty();
}
}