javaslang.Value Maven / Gradle / Ivy
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ 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 extends T>} 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 extends T> 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 super T, ? super U> 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 super T> 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 super T> 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 super T> 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 extends T> 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 extends T> 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 super T, ? extends U> 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 super T> 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 super T, ? extends Tuple2 extends K, ? extends V>> 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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy