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

javaslang.Value Maven / Gradle / Ivy

There is a newer version: 8.1.2
Show newest version
/*     / \____  _    _  ____   ______  / \ ____  __    _______
 *    /  /    \/ \  / \/    \ /  /\__\/  //    \/  \  //  /\__\   JΛVΛSLΛNG
 *  _/  /  /\  \  \/  /  /\  \\__\\  \  //  /\  \ /\\/ \ /__\ \   Copyright 2014-2016 Javaslang, http://javaslang.io
 * /___/\_/  \_/\____/\_/  \_/\__\/__/\__\_/  \_//  \__/\_____/   Licensed under the Apache License, Version 2.0
 */
package javaslang;

import javaslang.collection.*;
import javaslang.control.Either;
import javaslang.control.Option;
import javaslang.control.Try;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Optional;
import java.util.function.*;
import java.util.stream.StreamSupport;

/**
 * Functional programming is all about values and transformation of values using functions. The {@code Value}
 * type reflects the values in a functional setting. It can be seen as the result of a partial function application.
 * Hence the result may be undefined. If a value is undefined, we say it is empty.
 * 

* How the empty state is interpreted depends on the context, i.e. it may be undefined, failed, * no elements, etc. *

* * Basic operations: * *

    *
  • {@link #get()}
  • *
  • {@link #getOption()}
  • *
  • {@link #getOrElse(Object)}
  • *
  • {@link #getOrElse(Supplier)}
  • *
  • {@link #getOrElseThrow(Supplier)}
  • *
  • {@link #isEmpty()}
  • *
  • {@link #isSingleValued()}
  • *
  • {@link #map(Function)}
  • *
  • {@link #stringPrefix()}
  • *
* * Equality checks: * *
    *
  • {@link #corresponds(Iterable, BiPredicate)}
  • *
  • {@link #eq(Object)}
  • *
* * Iterable extensions: * *
    *
  • {@link #contains(Object)}
  • *
  • {@link #exists(Predicate)}
  • *
  • {@link #forAll(Predicate)}
  • *
  • {@link #forEach(Consumer)}
  • *
  • {@link #iterator()}
  • *
* * Side-effects: * *
    *
  • {@link #out(PrintStream)}
  • *
  • {@link #out(PrintWriter)}
  • *
  • {@link #peek(Consumer)}
  • *
  • {@link #stderr()}
  • *
  • {@link #stdout()}
  • *
* * Type conversion: * *
    *
  • {@link #toArray()}
  • *
  • {@link #toCharSeq()}
  • *
  • {@link #toJavaArray()}
  • *
  • {@link #toJavaArray(Class)}
  • *
  • {@link #toJavaCollection(Supplier)}
  • *
  • {@link #toJavaList()}
  • *
  • {@link #toJavaList(Supplier)}
  • *
  • {@link #toJavaMap(Function)}
  • *
  • {@link #toJavaMap(Supplier, Function)}
  • *
  • {@link #toJavaOptional()}
  • *
  • {@link #toJavaSet()}
  • *
  • {@link #toJavaSet(Supplier)}
  • *
  • {@link #toJavaStream()}
  • *
  • {@link #toLeft(Object)}
  • *
  • {@link #toLeft(Supplier)}
  • *
  • {@link #toList()}
  • *
  • {@link #toMap(Function)}
  • *
  • {@link #toOption()}
  • *
  • {@link #toQueue()}
  • *
  • {@link #toRight(Object)}
  • *
  • {@link #toRight(Supplier)}
  • *
  • {@link #toSet()}
  • *
  • {@link #toStack()}
  • *
  • {@link #toStream()}
  • *
  • {@link #toString()}
  • *
  • {@link #toTree()}
  • *
  • {@link #toTry()}
  • *
  • {@link #toTry(Supplier)}
  • *
  • {@link #toVector()}
  • *
* * Please note: flatMap signatures are manifold and have to be declared by subclasses of Value. * * @param The type of the wrapped value. * @author Daniel Dietrich * @since 2.0.0 */ public interface Value extends Iterable { /** * Narrows a widened {@code Value} to {@code Value} * by performing a type safe-cast. This is eligible because immutable/read-only * collections are covariant. * * @param value A {@code Value}. * @param Component type of the {@code Value}. * @return the given {@code value} instance as narrowed type {@code Value}. */ @SuppressWarnings("unchecked") static Value narrow(Value value) { return (Value) value; } /** * Shortcut for {@code exists(e -> Objects.equals(e, element))}, tests if the given {@code element} is contained. * * @param element An Object of type A, may be null. * @return true, if element is contained, false otherwise. */ default boolean contains(T element) { return exists(e -> Objects.equals(e, element)); } /** * Tests whether every element of this iterable relates to the corresponding element of another iterable by * satisfying a test predicate. * * @param Component type of that iterable * @param that the other iterable * @param predicate the test predicate, which relates elements from both iterables * @return {@code true} if both iterables have the same length and {@code predicate(x, y)} * is {@code true} for all corresponding elements {@code x} of this iterable and {@code y} of {@code that}, * otherwise {@code false}. */ default boolean corresponds(Iterable that, BiPredicate predicate) { final java.util.Iterator it1 = iterator(); final java.util.Iterator it2 = that.iterator(); while (it1.hasNext() && it2.hasNext()) { if (!predicate.test(it1.next(), it2.next())) { return false; } } return !it1.hasNext() && !it2.hasNext(); } /** * A smoothing replacement for {@code equals}. It is similar to Scala's {@code ==} but better in the way * that it is not limited to collection types, e.g. {@code Some(1) eq List(1)}, {@code None eq Failure(x)} etc. *

* In a nutshell: eq checks congruence of structures and equality of contained values. *

* Example: * *


     * // ((1, 2), ((3))) => structure: (()(())) values: 1, 2, 3
     * final Value<?> i1 = List.of(List.of(1, 2), Arrays.asList(List.of(3)));
     * final Value<?> i2 = Queue.of(Stream.of(1, 2), List.of(Lazy.of(() -> 3)));
     * assertThat(i1.eq(i2)).isTrue();
     * 
* * Semantics: * *

     * o == this             : true
     * o instanceof Value    : iterable elements are eq, non-iterable elements equals, for all (o1, o2) in (this, o)
     * o instanceof Iterable : this eq Iterator.of((Iterable<?>) o);
     * otherwise             : false
     * 
* * @param o An object * @return true, if this equals o according to the rules defined above, otherwise false. */ default boolean eq(Object o) { if (o == this) { return true; } else if (o instanceof Value) { final Value that = (Value) o; return this.iterator().corresponds(that.iterator(), (o1, o2) -> { if (o1 instanceof Value) { return ((Value) o1).eq(o2); } else if (o2 instanceof Value) { return ((Value) o2).eq(o1); } else { return Objects.equals(o1, o2); } }); } else if (o instanceof Iterable) { final Value that = Iterator.ofAll((Iterable) o); return this.eq(that); } else { return false; } } /** * Checks, if an element exists such that the predicate holds. * * @param predicate A Predicate * @return true, if predicate holds for one or more elements, false otherwise * @throws NullPointerException if {@code predicate} is null */ default boolean exists(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); for (T t : this) { if (predicate.test(t)) { return true; } } return false; } /** * Checks, if the given predicate holds for all elements. * * @param predicate A Predicate * @return true, if the predicate holds for all elements, false otherwise * @throws NullPointerException if {@code predicate} is null */ default boolean forAll(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return !exists(predicate.negate()); } /** * Performs an action on each element. * * @param action A {@code Consumer} * @throws NullPointerException if {@code action} is null */ @Override default void forEach(Consumer action) { Objects.requireNonNull(action, "action is null"); for (T t : this) { action.accept(t); } } /** * Gets the underlying value or throws if no value is present. * * @return the underlying value * @throws java.util.NoSuchElementException if no value is defined */ T get(); /** * Gets the underlying value as Option. * * @return Some(value) if a value is present, None otherwise */ default Option getOption() { return isEmpty() ? Option.none() : Option.some(get()); } /** * Returns the underlying value if present, otherwise {@code other}. * * @param other An alternative value. * @return A value of type {@code T} */ default T getOrElse(T other) { return isEmpty() ? other : get(); } /** * Returns the underlying value if present, otherwise {@code other}. * * @param supplier An alternative value supplier. * @return A value of type {@code T} * @throws NullPointerException if supplier is null */ default T getOrElse(Supplier supplier) { Objects.requireNonNull(supplier, "supplier is null"); return isEmpty() ? supplier.get() : get(); } /** * Returns the underlying value if present, otherwise throws {@code supplier.get()}. * * @param a Throwable type * @param supplier An exception supplier. * @return A value of type {@code T}. * @throws NullPointerException if supplier is null * @throws X if no value is present */ default T getOrElseThrow(Supplier supplier) throws X { Objects.requireNonNull(supplier, "supplier is null"); if (isEmpty()) { throw supplier.get(); } else { return get(); } } /** * Returns the underlying value if present, otherwise returns the result of {@code Try.of(supplier).get()}. * * @param supplier An alternative value supplier. * @return A value of type {@code T}. * @throws NullPointerException if supplier is null * @throws Try.NonFatalException containing the original exception if this Value was empty and the Try failed. */ default T getOrElseTry(Try.CheckedSupplier supplier) { Objects.requireNonNull(supplier, "supplier is null"); return isEmpty() ? Try.of(supplier).get() : get(); } /** * Checks, this {@code Value} is empty, i.e. if the underlying value is absent. * * @return false, if no underlying value is present, true otherwise. */ boolean isEmpty(); /** * States whether this is a single-valued type. * * @return {@code true} if this is single-valued, {@code false} otherwise. */ boolean isSingleValued(); /** * Maps the underlying value to a different component type. * * @param mapper A mapper * @param The new component type * @return A new value */ Value map(Function mapper); /** * Performs the given {@code action} on the first element if this is an eager implementation. * Performs the given {@code action} on all elements (the first immediately, successive deferred), * if this is a lazy implementation. * * @param action The action that will be performed on the element(s). * @return this instance */ Value peek(Consumer action); /** * Returns the name of this Value type, which is used by toString(). * * @return This type name. */ String stringPrefix(); // -- output /** * Sends the string representations of this to the {@link PrintStream}. * If this value consists of multiple elements, each element is displayed in a new line. * * @param out The PrintStream to write to * @throws IllegalStateException if {@code PrintStream.checkError()} is true after writing to stream. */ default void out(PrintStream out) { for (T t : this) { out.println(String.valueOf(t)); if (out.checkError()) { throw new IllegalStateException("Error writing to PrintStream"); } } } /** * Sends the string representations of this to the {@link PrintWriter}. * If this value consists of multiple elements, each element is displayed in a new line. * * @param writer The PrintWriter to write to * @throws IllegalStateException if {@code PrintWriter.checkError()} is true after writing to writer. */ default void out(PrintWriter writer) { for (T t : this) { writer.println(String.valueOf(t)); if (writer.checkError()) { throw new IllegalStateException("Error writing to PrintWriter"); } } } /** * Sends the string representations of this to the standard error stream {@linkplain System#err}. * If this value consists of multiple elements, each element is displayed in a new line. * * @throws IllegalStateException if {@code PrintStream.checkError()} is true after writing to stderr. */ default void stderr() { out(System.err); } /** * Sends the string representations of this to the standard output stream {@linkplain System#out}. * If this value consists of multiple elements, each element is displayed in a new line. * * @throws IllegalStateException if {@code PrintStream.checkError()} is true after writing to stdout. */ default void stdout() { out(System.out); } // -- Adjusted return types of Iterable /** * Returns a rich {@code javaslang.collection.Iterator}. * * @return A new Iterator */ @Override Iterator iterator(); // -- conversion methods /** * Converts this to a {@link Array}. * * @return A new {@link Array}. */ default Array toArray() { return ValueModule.toTraversable(this, Array.empty(), Array::of, Array::ofAll); } /** * Converts this to a {@link CharSeq}. * * @return A new {@link CharSeq}. */ default CharSeq toCharSeq() { return CharSeq.of(toString()); } /** * Converts this to a specific {@link java.util.Collection}. * * @param factory A {@code java.util.Collection} factory * @param a sub-type of {@code java.util.Collection} * @return a new {@code java.util.Collection} of type {@code C} */ default > C toJavaCollection(Supplier factory) { return ValueModule.toJavaCollection(this, factory.get()); } /** * Converts this to an untyped Java array. * * @return A new Java array. */ default Object[] toJavaArray() { return toJavaList().toArray(); } /** * Converts this to a typed Java array. * * @param componentType Component type of the array * @return A new Java array. * @throws NullPointerException if componentType is null */ @SuppressWarnings("unchecked") default T[] toJavaArray(Class componentType) { Objects.requireNonNull(componentType, "componentType is null"); if (componentType.isPrimitive()) { final Class boxedType = componentType == boolean.class ? Boolean.class : componentType == byte.class ? Byte.class : componentType == char.class ? Character.class : componentType == double.class ? Double.class : componentType == float.class ? Float.class : componentType == int.class ? Integer.class : componentType == long.class ? Long.class : componentType == short.class ? Short.class : componentType == void.class ? Void.class : null; return toJavaArray((Class) boxedType); } else { final java.util.List list = toJavaList(); return list.toArray((T[]) java.lang.reflect.Array.newInstance(componentType, list.size())); } } /** * Converts this to an {@link java.util.List}. * * @return A new {@link java.util.ArrayList}. */ default java.util.List toJavaList() { return ValueModule.toJavaCollection(this, new ArrayList<>()); } /** * Converts this to a specific {@link java.util.List}. * * @param factory A {@code java.util.List} factory * @param a sub-type of {@code java.util.List} * @return a new {@code java.util.List} of type {@code LIST} */ default > LIST toJavaList(Supplier factory) { return ValueModule.toJavaCollection(this, factory.get()); } /** * Converts this to a {@link java.util.Map}. * * @param f A function that maps an element to a key/value pair represented by Tuple2 * @param The key type * @param The value type * @return A new {@link java.util.HashMap}. */ default java.util.Map toJavaMap(Function> f) { return toJavaMap(java.util.HashMap::new, f); } /** * Converts this to a specific {@link java.util.Map}. * * @param factory A {@code java.util.Map} factory * @param f A function that maps an element to a key/value pair represented by Tuple2 * @param The key type * @param The value type * @param a sub-type of {@code java.util.Map} * @return a new {@code java.util.Map} of type {@code MAP} */ default > MAP toJavaMap(Supplier factory, Function> f) { Objects.requireNonNull(f, "f is null"); final MAP map = factory.get(); if (!isEmpty()) { if (isSingleValued()) { final Tuple2 entry = f.apply(get()); map.put(entry._1, entry._2); } else { for (T a : this) { final Tuple2 entry = f.apply(a); map.put(entry._1, entry._2); } } } return map; } /** * Converts this to an {@link java.util.Optional}. * * @return A new {@link java.util.Optional}. */ default Optional toJavaOptional() { return isEmpty() ? Optional.empty() : Optional.ofNullable(get()); } /** * Converts this to a {@link java.util.Set}. * * @return A new {@link java.util.HashSet}. */ default java.util.Set toJavaSet() { return ValueModule.toJavaCollection(this, new java.util.HashSet<>()); } /** * Converts this to a specific {@link java.util.Set}. * * @param factory A {@code java.util.Set} factory * @param a sub-type of {@code java.util.Set} * @return a new {@code java.util.Set} of type {@code SET} */ default > SET toJavaSet(Supplier factory) { return ValueModule.toJavaCollection(this, factory.get()); } /** * Converts this to a {@link java.util.stream.Stream}. * * @return A new {@link java.util.stream.Stream}. */ default java.util.stream.Stream toJavaStream() { return StreamSupport.stream(spliterator(), false); } /** * Converts this to a {@link Either}. * * @param right type * @param right A supplier of a right value * @return A new {@link Either.Right} containing the result of {@code right} if this is empty, otherwise * a new {@link Either.Left} containing this value. * @throws NullPointerException if {@code right} is null */ default Either toLeft(Supplier right) { Objects.requireNonNull(right, "right is null"); return isEmpty() ? Either.right(right.get()) : Either.left(get()); } /** * Converts this to a {@link Either}. * * @param right type * @param right An instance of a right value * @return A new {@link Either.Right} containing the value of {@code right} if this is empty, otherwise * a new {@link Either.Left} containing this value. * @throws NullPointerException if {@code right} is null */ default Either toLeft(R right) { return isEmpty() ? Either.right(right) : Either.left(get()); } /** * Converts this to a {@link List}. * * @return A new {@link List}. */ default List toList() { return ValueModule.toTraversable(this, List.empty(), List::of, List::ofAll); } /** * Converts this to a {@link Map}. * * @param f A function that maps an element to a key/value pair represented by Tuple2 * @param The key type * @param The value type * @return A new {@link HashMap}. */ default Map toMap(Function> f) { Objects.requireNonNull(f, "f is null"); if (isEmpty()) { return HashMap.empty(); } else if (isSingleValued()) { return HashMap.of(f.apply(get())); } else { return HashMap.ofEntries(Iterator.ofAll(this).map(f)); } } /** * Converts this to an {@link Option}. * * @return A new {@link Option}. */ default Option toOption() { if (this instanceof Option) { return (Option) this; } else { return getOption(); } } /** * Converts this to a {@link Queue}. * * @return A new {@link Queue}. */ default Queue toQueue() { return ValueModule.toTraversable(this, Queue.empty(), Queue::of, Queue::ofAll); } /** * Converts this to a {@link Either}. * * @param left type * @param left A supplier of a left value * @return A new {@link Either.Left} containing the result of {@code left} if this is empty, otherwise * a new {@link Either.Right} containing this value. * @throws NullPointerException if {@code left} is null */ default Either toRight(Supplier left) { Objects.requireNonNull(left, "left is null"); return isEmpty() ? Either.left(left.get()) : Either.right(get()); } /** * Converts this to a {@link Either}. * * @param left type * @param left An instance of a left value * @return A new {@link Either.Left} containing the value of {@code left} if this is empty, otherwise * a new {@link Either.Right} containing this value. * @throws NullPointerException if {@code left} is null */ default Either toRight(L left) { return isEmpty() ? Either.left(left) : Either.right(get()); } /** * Converts this to a {@link Set}. * * @return A new {@link HashSet}. */ default Set toSet() { return ValueModule.toTraversable(this, HashSet.empty(), HashSet::of, HashSet::ofAll); } /** * Converts this to a {@link Stack}. * * @return A new {@link List}, which is a {@link Stack}. */ default Stack toStack() { return toList(); } /** * Converts this to a {@link Stream}. * * @return A new {@link Stream}. */ default Stream toStream() { return ValueModule.toTraversable(this, Stream.empty(), Stream::of, Stream::ofAll); } /** * Converts this to a {@link Try}. *

* If this value is undefined, i.e. empty, then a new {@code Failure(NoSuchElementException)} is returned, * otherwise a new {@code Success(value)} is returned. * * @return A new {@link Try}. */ default Try toTry() { if (this instanceof Try) { return (Try) this; } else { return Try.of(this::get); } } /** * Converts this to a {@link Try}. *

* If this value is undefined, i.e. empty, then a new {@code Failure(ifEmpty.get())} is returned, * otherwise a new {@code Success(value)} is returned. * * @param ifEmpty an exception supplier * @return A new {@link Try}. */ default Try toTry(Supplier ifEmpty) { Objects.requireNonNull(ifEmpty, "ifEmpty is null"); return isEmpty() ? Try.failure(ifEmpty.get()) : toTry(); } /** * Converts this to a {@link Tree}. * * @return A new {@link Tree}. */ default Tree toTree() { return ValueModule.toTraversable(this, Tree.empty(), Tree::of, Tree::ofAll); } /** * Converts this to a {@link Vector}. * * @return A new {@link Vector}. */ default Vector toVector() { return ValueModule.toTraversable(this, Vector.empty(), Vector::of, Vector::ofAll); } // -- Object /** * Clarifies that values have a proper equals() method implemented. *

* See Object.equals(Object). * * @param o An object * @return true, if this equals o, false otherwise */ @Override boolean equals(Object o); /** * Clarifies that values have a proper hashCode() method implemented. *

* See Object.hashCode(). * * @return The hashcode of this object */ @Override int hashCode(); /** * Clarifies that values have a proper toString() method implemented. *

* See Object.toString(). * * @return A String representation of this object */ @Override String toString(); } interface ValueModule { static , V> T toTraversable(Value value, T empty, Function ofElement, Function, T> ofAll) { if (value.isEmpty()) { return empty; } else if (value.isSingleValued()) { return ofElement.apply(value.get()); } else { return ofAll.apply(value); } } static , V> T toJavaCollection(Value value, T empty) { if (!value.isEmpty()) { if (value.isSingleValued()) { empty.add(value.get()); } else { value.forEach(empty::add); } } return empty; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy