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

io.vavr.control.Option Maven / Gradle / Ivy

The newest version!
/* ____  ______________  ________________________  __________
 * \   \/   /      \   \/   /   __/   /      \   \/   /      \
 *  \______/___/\___\______/___/_____/___/\___\______/___/\___\
 *
 * Copyright 2021 Vavr, https://vavr.io
 *
 * 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.vavr.control;

import io.vavr.PartialFunction;
import io.vavr.collection.Iterator;
import io.vavr.collection.Seq;
import io.vavr.collection.Vector;

import java.io.Serializable;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Replacement for {@link java.util.Optional}.
 * 

* Option is a monadic container type which * represents an optional value. Instances of Option are either an instance of {@link Some} or the * singleton {@link None}. *

* Most of the API is taken from {@link java.util.Optional}. A similar type can be found in Haskell and Scala. * * @param The type of the optional value. */ @SuppressWarnings("deprecation") public abstract class Option implements Iterable, io.vavr.Value, Serializable { private static final long serialVersionUID = 1L; // sealed private Option() {} /** * Creates a new {@code Option} of a given value. * *

{@code
     * // = Some(3), an Option which contains the value 3
     * Option option = Option.of(3);
     *
     * // = None, the empty Option
     * Option none = Option.of(null);
     * }
* * @param value A value * @param type of the value * @return {@code Some(value)} if value is not {@code null}, {@code None} otherwise */ public static Option of(T value) { return (value == null) ? none() : some(value); } /** * Reduces many {@code Option}s into a single {@code Option} by transforming an * {@code Iterable>} into a {@code Option>}. If any of * the Options are {@link Option.None}, then this returns {@link Option.None}. * *
{@code
     * Seq> seq = Vector.of(Option.of(1), Option.of(2), Option.of(3));
     *
     * // = Some(Seq(1, 2, 3))
     * Option> option = Option.sequence(seq);
     *
     * Seq> seq = Vector.of(Option.of(1), Option.none());
     *
     * // = None since some elements in the Iterable are None
     * Option> option = Option.sequence(seq);
     * }
* @param values An {@code Iterable} of {@code Option}s * @param type of the Options * @return An {@code Option} of a {@link Seq} of results * @throws NullPointerException if {@code values} is null */ public static Option> sequence(Iterable> values) { Objects.requireNonNull(values, "values is null"); Vector vector = Vector.empty(); for (Option value : values) { if (value.isEmpty()) { return Option.none(); } vector = vector.append(value.get()); } return Option.some(vector); } /** * Maps the values of an iterable to a sequence of mapped values into a single {@code Option} by * transforming an {@code Iterable} into a {@code Option>}. * *
{@code
     * Function> mapper = i -> {
     *      if (i <= 0) {
     *          return Option.none();
     *      }
     *      return Option.of("a" = i.toString());
     * }
     *
     * // = Some(Seq("a1", "a2", "a3"))
     * Option> option = traverse(Vector.of(1, 2, 3), mapper);
     *
     * // = None
     * Option> none = traverse(Vector.of(-1, 0, 1), mapper);
     * }
* * @param values An {@code Iterable} of values. * @param mapper A mapper of values to Options * @param The type of the given values. * @param The mapped value type. * @return A {@code Option} of a {@link Seq} of results. * @throws NullPointerException if values or f is null. */ public static Option> traverse(Iterable values, Function> mapper) { Objects.requireNonNull(values, "values is null"); Objects.requireNonNull(mapper, "mapper is null"); return sequence(Iterator.ofAll(values).map(mapper)); } /** * Creates a new {@code Some} of a given value. *

* The only difference to {@link Option#of(Object)} is, when called with argument {@code null}. * *

{@code
     * // = Some(3)
     * Option.some(3);
     *
     * // = Some(null)
     * Option.some(null);
     * }
* * @param value A value * @param type of the value * @return {@code Some(value)} */ public static Option some(T value) { return new Some<>(value); } /** * Returns the single instance of {@code None} * *
{@code
     * // = None
     * Option none = Option.none();
     * }
* * @param component type * @return the single instance of {@code None} */ public static Option none() { @SuppressWarnings("unchecked") final None none = (None) None.INSTANCE; return none; } /** * Narrows a widened {@code Option} to {@code Option} * by performing a type-safe cast. This is eligible because immutable/read-only * collections are covariant. * *
{@code
     * Option option = Option.of(3);
     * // Narrow to an Option of Number
     * Option narrowed = Option.narrow(option);
     * }
* * @param option A {@code Option}. * @param Component type of the {@code Option}. * @return the given {@code option} instance as narrowed type {@code Option}. */ @SuppressWarnings("unchecked") public static Option narrow(Option option) { return (Option) option; } /** * Creates {@code Some} of suppliers value if condition is true, or {@code None} in other case * *
{@code
     * Supplier supplier = () -> "supplied";
     *
     * // = Some("supplied")
     * Option supplied = Option.when(true, supplier);
     *
     * // = None
     * Option none = Option.when(false, supplier);
     * }
* * @param type of the optional value * @param condition A boolean value * @param supplier An optional value supplier, may supply {@code null} * @return return {@code Some} of supplier's value if condition is true, or {@code None} in other case * @throws NullPointerException if the given {@code supplier} is null */ public static Option when(boolean condition, Supplier supplier) { Objects.requireNonNull(supplier, "supplier is null"); return condition ? some(supplier.get()) : none(); } /** * Creates {@code Some} of value if condition is true, or {@code None} in other case * *
{@code
     * // = Some(5)
     * Option option = Option.when(true, 5);
     *
     * // = None
     * Option none = Option.when(false, 5);
     * }
* * @param type of the optional value * @param condition A boolean value * @param value An optional value, may be {@code null} * @return return {@code Some} of value if condition is true, or {@code None} in other case */ public static Option when(boolean condition, T value) { return condition ? some(value) : none(); } /** * Wraps a Java Optional to a new Option * *
{@code
     * Optional optional = Optional.ofNullable("value");
     *
     * // Make a Some("value") from an Optional
     * Option option = Option.ofOptional(optional);
     *
     * Optional empty = Optional.empty();
     *
     * // Make a None from an empty Optional
     * Option none = Option.ofOptional(empty);
     * }
* * @param optional a given optional to wrap in {@code Option} * @param type of the value * @return {@code Some(optional.get())} if value is Java {@code Optional} is present, {@code None} otherwise */ @SuppressWarnings("OptionalUsedAsFieldOrParameterType") public static Option ofOptional(Optional optional) { Objects.requireNonNull(optional, "optional is null"); return optional.>map(Option::of).orElseGet(Option::none); } /** * Collects value that is in the domain of the given {@code partialFunction} by mapping the value to type {@code R}. * *
{@code
     * partialFunction.isDefinedAt(value)
     * }
* * If the element makes it through that filter, the mapped instance is wrapped in {@code Option} * *
{@code
     * R newValue = partialFunction.apply(value)
     * }
* * * @param partialFunction A function that is not necessarily defined on value of this option. * @param The new value type * @return A new {@code Option} instance containing value of type {@code R} * @throws NullPointerException if {@code partialFunction} is null */ public final Option collect(PartialFunction partialFunction) { Objects.requireNonNull(partialFunction, "partialFunction is null"); return flatMap(partialFunction.lift()); } /** * Returns true, if this is {@code None}, otherwise false, if this is {@code Some}. * *
{@code
     * // Prints "false"
     * System.out.println(Option.of(10).isEmpty());
     *
     * // Prints "true"
     * System.out.println(Option.none().isEmpty());
     * }
* * @return true, if this {@code Option} is empty, false otherwise */ @Override public abstract boolean isEmpty(); /** * Runs a Java Runnable passed as parameter if this {@code Option} is empty. * *
{@code
     * Runnable print = () -> System.out.println("Option is empty");
     *
     * // Prints nothing
     * Option.of("value").onEmpty(print);
     *
     * // Prints "Option is empty"
     * Option.none().onEmpty(print);
     * }
* * @param action a given Runnable to be run * @return this {@code Option} */ public final Option onEmpty(Runnable action) { Objects.requireNonNull(action, "action is null"); if (isEmpty()) { action.run(); } return this; } /** * An {@code Option}'s value is computed synchronously. * *
{@code
     * // Prints "false"
     * System.out.println(Option.of(1).isAsync());
     *
     * // Prints "false"
     * System.out.println(Option.none().isAsync());
     * }
* * @return false */ @Override public final boolean isAsync() { return false; } /** * Returns true, if this is {@code Some}, otherwise false, if this is {@code None}. *

* Please note that it is possible to create {@code new Some(null)}, which is defined. * *

{@code
     * // Prints "true"
     * System.out.println(Option.of(10).isDefined());
     *
     * // Prints "false"
     * System.out.println(Option.none().isDefined());
     *
     * // Prints "true
     * System.out.println(Option.of(null).isDefined());
     * }
* * @return true, if this {@code Option} has a defined value, false otherwise */ public final boolean isDefined() { return !isEmpty(); } /** * An {@code Option}'s value is computed eagerly. * *
{@code
     * // Prints "false"
     * System.out.println(Option.of(3.14).isLazy());
     *
     * // Prints "false"
     * System.out.println(Option.none().isLazy());
     * }
* * @return false */ @Override public final boolean isLazy() { return false; } /** * An {@code Option} is single-valued. * *
{@code
     * // Prints "true"
     * System.out.println(Option.of("value").isSingleValued());
     *
     * // Prints "true"
     * System.out.println(Option.none().isSingleValued());
     * }
* * @return {@code true} */ @Override public final boolean isSingleValued() { return true; } /** * Gets the value if this is a {@code Some} or throws if this is a {@code None}. * *
{@code
     * // Prints "57"
     * System.out.println(Option.of(57).get());
     *
     * // Throws a NoSuchElementException
     * Option.none().get();
     * }
* * @return the value * @throws NoSuchElementException if this is a {@code None}. */ @Override public abstract T get(); /** * Returns the value if this is a {@code Some} or the {@code other} value if this is a {@code None}. *

* Please note, that the other value is eagerly evaluated. * *

{@code
     * // Prints "Hello"
     * System.out.println(Option.of("Hello").getOrElse("World"));
     *
     * // Prints "World"
     * Option.none().getOrElse("World");
     * }
* * @param other An alternative value * @return This value, if this Option is defined or the {@code other} value, if this Option is empty. */ @Override public final T getOrElse(T other) { return isEmpty() ? other : get(); } /** * Returns this {@code Option} if it is nonempty, otherwise return the alternative. * *
{@code
     * Option other = Option.of("Other");
     *
     * // = Some("Hello World")
     * Option.of("Hello World").orElse(other);
     *
     * // = Some("Other")
     * Option.none().orElse(other);
     * }
* * @param other An alternative {@code Option} * @return this {@code Option} if it is nonempty, otherwise return the alternative. */ @SuppressWarnings("unchecked") public final Option orElse(Option other) { Objects.requireNonNull(other, "other is null"); return isEmpty() ? (Option) other : this; } /** * Returns this {@code Option} if it is nonempty, otherwise return the result of evaluating supplier. * *
{@code
     * Supplier> supplier = () -> Option.of(5);
     *
     * // = Some(2)
     * Option.of(2).orElse(supplier);
     *
     * // = Some(5)
     * Option.none().orElse(supplier);
     * }
* * @param supplier An alternative {@code Option} supplier * @return this {@code Option} if it is nonempty, otherwise return the result of evaluating supplier. */ @SuppressWarnings("unchecked") public final Option orElse(Supplier> supplier) { Objects.requireNonNull(supplier, "supplier is null"); return isEmpty() ? (Option) supplier.get() : this; } /** * Returns this {@code Option} if this is defined, or {@code null} if it is empty. * *
{@code
     * // = Some("Hello World")
     * Option.of("Hello World").orNull();
     *
     * // = null
     * Option.none().orNull();
     * }
* * @return this value if it is defined, or {@code null} if it is empty. */ public final T orNull() { return isEmpty() ? null : get(); } /** * Returns the value if this is a {@code Some}, otherwise {@code supplier.get()} is returned. *

* Please note, that the alternate value is lazily evaluated. * *

{@code
     * Supplier supplier = () -> 5.342;
     *
     * // = 1.2
     * Option.of(1.2).getOrElse(supplier);
     *
     * // = 5.342
     * Option.none().getOrElse(supplier);
     * }
* * @param supplier An alternative value supplier * @return This value, if this Option is defined or the {@code other} value, if this Option is empty. */ @Override public final T getOrElse(Supplier supplier) { Objects.requireNonNull(supplier, "supplier is null"); return isEmpty() ? supplier.get() : get(); } /** * Returns the value if this is a {@code Some}, otherwise throws an exception. * *
{@code
     * Supplier supplier = () -> new RuntimeException();
     *
     * // = 12
     * Option.of(12).getOrElseThrow(supplier);
     *
     * // throws RuntimeException
     * Option.none().getOrElseThrow(supplier);
     * }
* * @param exceptionSupplier An exception supplier * @param A throwable * @return This value, if this Option is defined, otherwise throws X * @throws X a throwable */ @Override public final T getOrElseThrow(Supplier exceptionSupplier) throws X { Objects.requireNonNull(exceptionSupplier, "exceptionSupplier is null"); if (isEmpty()) { throw exceptionSupplier.get(); } else { return get(); } } /** * Returns {@code Some(value)} if this is a {@code Some} and the value satisfies the given predicate. * Otherwise {@code None} is returned. * *
{@code
     * Predicate isLessThanTen = i -> i < 10;
     *
     * // = Some(8)
     * Option.some(8).filter(isLessThanTen);
     *
     * // = None
     * Option.some(12).filter(isLessThanTen);
     *
     * // = None
     * Option.none().filter(isLessThanTen);
     * }
* * @param predicate A predicate which is used to test an optional value * @return {@code Some(value)} or {@code None} as specified */ public final Option filter(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return isEmpty() || predicate.test(get()) ? this : none(); } /** * Returns {@code Some(value)} if this is a {@code Some} and the value not satisfies the given predicate. * Otherwise {@code None} is returned. * *
{@code
     * Predicate isEven = i -> (i & 1) == 0;
     *
     * // = Some(5)
     * Option.some(5).filterNot(isEven);
     *
     * // = None
     * Option.some(12).filterNot(isEven);
     *
     * // = None
     * Option.none().filterNot(isEven);
     * }
* * @param predicate A predicate which is used to test an optional value * @return {@code Some(value)} or {@code None} as specified */ public final Option filterNot(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return filter(predicate.negate()); } /** * Maps the value to a new {@code Option} if this is a {@code Some}, otherwise returns {@code None}. * *
{@code
     * Function> mapper = i -> i < 10 ? Option.of(i * 2) : Option.none();
     *
     * // = Some(14)
     * Option.of(7).flatMap(mapper);
     *
     * // = None
     * Option.of(11).flatMap(mapper);
     *
     * // = None
     * Option.none().flatMap(mapper);
     * }
* * @param mapper A mapper * @param Component type of the resulting Option * @return a new {@code Option} */ @SuppressWarnings("unchecked") public final Option flatMap(Function> mapper) { Objects.requireNonNull(mapper, "mapper is null"); return isEmpty() ? none() : (Option) mapper.apply(get()); } /** * Maps the value and wraps it in a new {@code Some} if this is a {@code Some}, otherwise returns a {@code None}. * *
{@code
     * Function mapper = s -> s + " World!";
     *
     * // = Some("Hello World!")
     * Option.of("Hello").map(mapper);
     *
     * // = None
     * Option.none().map(mapper);
     * }
* * @param mapper A value mapper * @param The new value type * @return a new {@code Some} containing the mapped value if this Option is defined, otherwise {@code None}, if this is empty. */ @Override public final Option map(Function mapper) { Objects.requireNonNull(mapper, "mapper is null"); return isEmpty() ? none() : some(mapper.apply(get())); } /** * Folds either the {@code None} or the {@code Some} side of the Option value. * *
{@code
     * Supplier ifNone = () -> 3.14;
     * Function mapper = s -> Double.valueOf(s) + 0.98;
     *
     * // = Some(4.98)
     * Option.of("4").fold(ifNone, mapper);
     *
     * // = Some(3.14)
     * Option.none().fold(ifNone, mapper);
     * }
* * @param ifNone maps the left value if this is a None * @param f maps the value if this is a Some * @param type of the folded value * @return A value of type U */ public final U fold(Supplier ifNone, Function f) { return this.map(f).getOrElse(ifNone); } /** * Performs the given {@code noneAction} if this option is not defined. * Performs the given {@code someAction} to this value, if this option is defined. * * @param noneAction The action that will be performed on the left element * @param someAction The action that will be performed on the right element * @return this instance */ public final Option peek(Runnable noneAction, Consumer someAction) { Objects.requireNonNull(noneAction, "noneAction is null"); Objects.requireNonNull(someAction, "someAction is null"); if (isEmpty()) { noneAction.run(); } else { someAction.accept(get()); } return this; } /** * Applies an action to this value, if this option is defined, otherwise does nothing. * *
{@code
     * Consumer print = i -> System.out.println(i);
     *
     * // Prints 5 and creates Some(8)
     * Option.of(5).peek(print).map(i -> i + 3);
     *
     * // Does not print anything
     * Option.none().peek(print);
     * }
* * @param action An action which can be applied to an optional value * @return this {@code Option} */ @Override public final Option peek(Consumer action) { Objects.requireNonNull(action, "action is null"); if (isDefined()) { action.accept(get()); } return this; } /** * Transforms this {@code Option}. * *
{@code
     * Function, String> f = o -> o.getOrElse(3).toString().concat("-transformed"));
     *
     * // Prints "1-transformed"
     * System.out.println(Option.of(1).transform(f));
     *
     * // Prints "3-transformed"
     * System.out.println(Option.none().transform(f));
     * }
* * @param f A transformation * @param Type of transformation result * @return An instance of type {@code U} * @throws NullPointerException if {@code f} is null */ public final U transform(Function, ? extends U> f) { Objects.requireNonNull(f, "f is null"); return f.apply(this); } @Override public final Iterator iterator() { return isEmpty() ? Iterator.empty() : Iterator.of(get()); } /** * Some represents a defined {@link Option}. It contains a value which may be null. However, to * create an Option containing null, {@code new Some(null)} has to be called. In all other cases * {@link Option#of(Object)} is sufficient. * * @param The type of the optional value. * @deprecated will be removed from the public API */ @Deprecated public static final class Some extends Option implements Serializable { private static final long serialVersionUID = 1L; private final T value; /** * Creates a new Some containing the given value. * * @param value A value, may be null */ private Some(T value) { this.value = value; } @Override public T get() { return value; } @Override public boolean isEmpty() { return false; } @Override public boolean equals(Object obj) { return (obj == this) || (obj instanceof Some && Objects.equals(value, ((Some) obj).value)); } @Override public int hashCode() { return Objects.hashCode(value); } @Override public String stringPrefix() { return "Some"; } @Override public String toString() { return stringPrefix() + "(" + value + ")"; } } /** * None is a singleton representation of the undefined {@link Option}. * * @param The type of the optional value. * @deprecated will be removed from the public API */ @Deprecated public static final class None extends Option implements Serializable { private static final long serialVersionUID = 1L; /** * The singleton instance of None. */ private static final None INSTANCE = new None<>(); /** * Hidden constructor. */ private None() { } @Override public T get() { throw new NoSuchElementException("No value present"); } @Override public boolean isEmpty() { return true; } @Override public boolean equals(Object o) { return o == this; } @Override public int hashCode() { return 1; } @Override public String stringPrefix() { return "None"; } @Override public String toString() { return stringPrefix(); } // -- Serializable implementation /** * Instance control for object serialization. * * @return The singleton instance of None. * @see Serializable */ private Object readResolve() { return INSTANCE; } } }