com.ajjpj.afoundation.collection.immutable.AOption Maven / Gradle / Ivy
package com.ajjpj.afoundation.collection.immutable;
import com.ajjpj.afoundation.collection.ACollectionHelper;
import com.ajjpj.afoundation.collection.AEquality;
import com.ajjpj.afoundation.function.*;
import java.io.Serializable;
import java.util.*;
/**
* This class represents an object that may or may not be present. It is intended as a more explicit, less error prone
* replacement for using null
to represent the absence of a value.
*
* There are two flavors of AOption
: AOption.some(...)
and AOption.none()
.
* AOption.some(x)
represents the presence of a value, namely 'x', while AOption.none()
* represents 'no value'.
*
* There are two key reasons for using this class instead of using null
to encode the absence of a value.
*
* Firstly, AOption
explicitly shows in the code that a value is optional, forcing code to explicitly deal
* with that fact. A method returning a value that may or may not be present (e.g. a call to get(...)
on
* a map) can specify AOption as its return type to make that characteristic explicit.
*
* Secondly, code can operate on an AOption
regardless of whether there is a value or not (e.g. by calling
* map()
). This functional style of programming can make for code that is easier to read and less cluttered
* than having if
statements checking for the presence of values all over the place.
*
* This class was inspired by the Option
class from the Scala standard library. Thanks for the excellent
* code, Scala team!
*
* @author arno
*/
public abstract class AOption implements ACollection, Serializable {
/**
* @return a 'some' filled with the literal passed in as a parameter
*/
public static AOption some(T el) {
return new ASome<>(el);
}
/**
* @return the 'none'
*/
@SuppressWarnings("unchecked")
public static AOption none() {
return (AOption) ANone.INSTANCE;
}
/**
* This is a convenience method that creates an AOption based on Java conventions.
*
* @return 'none()' if the parameter is null, 'some(nullable)' otherwise
*/
public static AOption fromNullable(T nullable) {
return nullable != null ? some(nullable) : AOption.none();
}
/**
* @return true if this AOption holds a value
*/
public abstract boolean isDefined();
/**
* @return true if this option does not hold a value
*/
public boolean isEmpty() {
return !isDefined();
}
/**
* @return the element held in this AOption if there is one; otherwise, a NoSuchElementException is thrown
*/
public abstract T get();
/**
* @return the element held in this AOption if there is one, and the default element passed in as parameter otherwise
*/
public abstract T getOrElse (T defaultValue);
/**
* @return the element held in this AOption if there is one, and the result of evaluating the function passed in otherwise. The function is
* guaranteed to be evaluated only if this AOption is empty.
*/
public abstract T getOrElseEval (AFunction0 producer) throws E;
/**
* This method returns the element contained in this AOption if it is defined. Otherwise, it evaluates the function passed in and throws the exception
* returned by that function. The function is guaranteed to be evaluated only if this AOption is empty.
*/
public abstract T getOrElseThrow (AFunction0NoThrow producer) throws E;
@Override public abstract AOption map(AFunction1 super T, ? extends X, E> f) throws E;
@Override public ACollection flatMap(AFunction1 super T, ? extends Iterable, E> f) throws E {
throw new UnsupportedOperationException("AOption can not be flattened");
}
@Override public ACollection flatten() {
throw new UnsupportedOperationException("AOption can not be flattened");
}
@Override public String mkString() {
return ACollectionHelper.mkString (this);
}
@Override public String mkString(String separator) {
return ACollectionHelper.mkString(this, separator);
}
@Override public String mkString(String prefix, String separator, String suffix) {
return ACollectionHelper.mkString(this, prefix, separator, suffix);
}
public abstract boolean equals(Object o);
public abstract int hashCode();
@Override public Collection asJavaUtilCollection () {
return new AbstractCollection () {
@SuppressWarnings ("NullableProblems")
@Override public Iterator iterator () {
return AOption.this.iterator ();
}
@Override public int size () {
return iterator ().hasNext () ? 1 : 0;
}
};
}
static class ASome extends AOption {
private final T el;
ASome(T el) {
this.el = el;
}
@Override public T get() {
return el;
}
@Override public T getOrElse (T defaultValue) {
return el;
}
@Override public T getOrElseEval (AFunction0 producer) throws E {
return el;
}
@Override public T getOrElseThrow (AFunction0NoThrow producer) throws E {
return el;
}
@Override public boolean isDefined() {
return true;
}
@Override public ACollection clear () {
return none ();
}
@Override public void foreach(AStatement1 super T, E> f) throws E {
f.apply(el);
}
@Override public AOption find(APredicate super T, E> pred) throws E {
return filter(pred);
}
@Override public AOption map(AFunction1 super T, ? extends X, E> f) throws E {
return some (f.apply (el));
}
@Override public ACollection collect (APartialFunction super T, ? extends X, E> pf) throws E {
if (pf.isDefinedAt (el)) {
return some (pf.apply (el));
} else {
return none();
}
}
@Override public AOption filter(APredicate super T, E> pred) throws E {
if(pred.apply(el))
return this;
else
return none();
}
@Override public R foldLeft (R startValue, AFunction2 f) throws E {
return f.apply(startValue, el);
}
@Override public int size() {
return 1;
}
@Override public boolean nonEmpty() {
return true;
}
@Override public boolean forAll(APredicate super T, E> pred) throws E {
return find(pred).isDefined();
}
@Override public boolean exists(APredicate super T, E> pred) throws E {
return find(pred).isDefined();
}
@Override public AMap> groupBy(AFunction1 super T, ? extends X, E> f) throws E {
return groupBy(f, AEquality.EQUALS);
}
@Override public AMap> groupBy(AFunction1 super T, ? extends X, E> f, AEquality keyEquality) throws E { //TODO junit
final AMap> result = AHashMap.empty(keyEquality);
return result.updated(f.apply(el), this);
}
@Override public AList toList() {
return AList.nil().cons(el);
}
@Override public ASet toSet() {
return AHashSet.empty ().added(el);
}
@Override public ASet toSet(AEquality equality) {
return AHashSet.empty (equality).added(el);
}
@SuppressWarnings("NullableProblems")
@Override public Iterator iterator() {
return Collections.singletonList(el).iterator();
}
@Override public String toString() {
return "AOption.some(" + el + ")";
}
@SuppressWarnings("RedundantIfStatement")
@Override public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ASome aSome = (ASome) o;
if (el != null ? !el.equals(aSome.el) : aSome.el != null) return false;
return true;
}
@Override public int hashCode() {
return el != null ? el.hashCode() : 0;
}
@Override public boolean contains(Object o) {
return AEquality.EQUALS.equals(el, o);
}
// @SuppressWarnings("NullableProblems")
// @Override public Object[] toArray() {
// return new Object[] {el};
// }
//
// @SuppressWarnings({"NullableProblems", "SuspiciousToArrayCall"})
// @Override public T1[] toArray(T1[] a) {
// return Arrays.asList(el).toArray(a);
// }
//
// @Override public boolean add(T t) {
// throw new UnsupportedOperationException();
// }
//
// @Override public boolean remove(Object o) {
// throw new UnsupportedOperationException();
// }
//
// @SuppressWarnings({"NullableProblems", "SimplifiableIfStatement"})
// @Override public boolean containsAll(Collection> c) {
// if(c.isEmpty()) {
// return true;
// }
// return c.size() == 1 && c.contains(el);
// }
//
// @SuppressWarnings("NullableProblems")
// @Override public boolean addAll(Collection extends T> c) {
// throw new UnsupportedOperationException();
// }
//
// @SuppressWarnings("NullableProblems")
// @Override public boolean removeAll(Collection> c) {
// throw new UnsupportedOperationException();
// }
//
// @SuppressWarnings("NullableProblems")
// @Override public boolean retainAll(Collection> c) {
// throw new UnsupportedOperationException();
// }
//
// @Override public void clear() {
// throw new UnsupportedOperationException();
// }
}
static class ANone extends AOption