Skip to content

Instantly share code, notes, and snippets.

@kiritsuku
Created March 7, 2012 17:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kiritsuku/1994681 to your computer and use it in GitHub Desktop.
Save kiritsuku/1994681 to your computer and use it in GitHub Desktop.
JDK1.8, Project Lambda, option type
import java.util.NoSuchElementException;
import java.util.functions.*;
/**
* Represents optional values. Instances of Option are either an instance of
* Some or the singleton object NONE.
* <p>
* The list dependency can be found at: https://gist.github.com/1989662
* <p>
* ATTENTION: This code compiles only with JDK1.8 and project lambda which can
* be found at http://jdk8.java.net/lambda/ and is not production ready.
*
* @version 0.1
* @since JDK1.6, Feb 4, 2012
* @param <A>
* an arbitrary type which specifies the the type the Option holds.
*/
public abstract class Option<A> {
/**
* The type NONE represents an empty Option. It does never hold a value.
*/
@SuppressWarnings("rawtypes")
public static final Option NONE = new Option() {
@Override
public Object get() {
throw new NoSuchElementException("None.get");
}
@Override
public boolean isDefined() {
return false;
}
@Override
public String toString() {
return "None";
}
};
/**
* Returns the singleton object NONE which does never hold a value.
*
* @param <B>
* the type which is adopted by NONE.
* @return the singleton object NONE.
*/
@SuppressWarnings("unchecked")
public static <B> Option<B> none() {
return NONE;
}
/**
* Creates a new Some. This method guarantees that the value saved by Some is
* never null.
*
* @param <B>
* the type which is adopted by Some.
* @param value
* the parameter which is wrapped by Some.
* @return the parameter some wrapped by Some.
*/
public static <B> Option<B> some(final B value) {
if (value == null) {
return none();
}
return new Some<B>(value);
}
/**
* Returns the value saved by the Option type. If Option is Some the value is
* returned. If it is NONE, an exception is thrown.
*
* @return the saved value
*/
public abstract A get();
/**
* Checks whether the Option type is of type Some or not. It returns always
* true for some and always false for NONE.
*
* @return true when the Option is defined
*/
public abstract boolean isDefined();
/**
* Returns the option's value if the option is not empty, otherwise the
* result of evaluating the function.
*
* @param f
* the default expression
*/
public A getOrElse(final Function0<A> f) {
return isDefined() ? get() : f.apply();
}
/**
* Returns this Option's value or null if it is not defined.
*/
public A orNull() {
return getOrElse(new Function0<A>() {
public A apply() {
return null;
}
});
// return getOrElse(() -> null);
}
/**
* Returns an option which contains a mapped value. If the Option is of type
* Some its value is unpacked, transformed and then returned packed in a new
* Some. But if the Option is None nothing is transformed and None is
* directly returned.
*
* @param <B>
* the new type of the value the Option type holds
* @param f
* the function to transform the value
*/
public <B> Option<B> map(final Mapper<A, B> f) {
return isDefined() ? some(f.map(get())) : Option.<B> none();
}
/**
* Returns the result of applying the function to this Option's value if this
* Option is not empty.
*
* @param <B>
* the new type of the value the Option type holds
* @param f
* the function to transform the value
*/
public <B> Option<B> flatMap(final Mapper<A, Option<B>> f) {
return isDefined() ? f.map(get()) : Option.<B> none();
}
/**
* Returns this Option if it is not empty applying the predicate to this
* Option's value returns true.
*
* @param f
* the predicate used for testing
*/
public Option<A> filter(final Predicate<A> f) {
return isDefined() && f.eval(get()) ? this : Option.<A> none();
}
/**
* Apply the given procedure to the Option's value it it is not empty.
*
* @param f
* the procedure to apply
*/
public void forEach(final Block<A> f) {
if (isDefined()) {
f.apply(get());
}
}
/**
* Checks whether this Option's value is defined and applying the predicate
* to this Option's value returns true.
*
* @param f
* the predicate used for testing
*/
public boolean exists(final Predicate<A> f) {
return isDefined() && f.eval(get());
}
/**
* Returns this Option if it is defined, otherwise returns the result of
* evaluating f.
*/
public Option<? super A> orElse(final Function0<Option<? super A>> f) {
if (isDefined()) {
return this;
}
return f.apply();
}
/**
* Returns a singleton list which holds this element or an empty list if this
* Option is not defined.
*/
@SuppressWarnings("unchecked")
public List<A> toList() {
return isDefined() ? List.of(get()) : List.<A> nil();
}
}
/**
* Type Some is a wrapper for arbitrary values. The value saved by Some can
* never be null.
*
* @version 0.1
* @since JDK1.6, Feb 4, 2012
* @param <A>
* an arbitrary type which specifies the the type the Option holds.
*/
final class Some<A> extends Option<A> {
private final A value;
/**
* Creates a new instance of Some.
*
* @param value
* the value
*/
public Some(final A value) {
if (value == null) {
throw new IllegalArgumentException(
"value is null. Use Option.none() instead");
}
this.value = value;
}
@Override
public A get() {
return value;
}
@Override
public boolean isDefined() {
return true;
}
@Override
public String toString() {
return "Some(" + value + ")";
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
return obj instanceof Some ? get().equals(((Some<?>) obj).get()) : false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment