package ch.akuhn.util; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; /** * Avoids void clutter in the presence of null. Your eyes do not deceive you, * this is Haskell's maybe monad in Java. Use it as follows. * * <pre> * public static void main(String[] args) { * for (String arg: Maybe.last(args)) { * System.out.println(arg); * } * * </pre> * * @author Adrian Kuhn, 2008/08/08 * @author Inspired by work by Tony Morris, 2006/11/13 * @author Inspired by work by Daniel Spiewak, 2008/08/07 * @author Inspired by work by Stephan Schmidt, 2008/08/06 * * @see http://blog.tmorris.net/maybe-in-java/ * @see http://www.codecommit.com/blog/scala/the-option-pattern * @see http * ://stephan.reposita.org/archives/2008/08/06/for-hack-with-option-monad * -in-java/ * */ public abstract class Maybe<T> implements Iterable<T> { private static final class None<T> extends Maybe<T> { private None() { } @Override public T get() { throw new NoSuchElementException("Cannot resolve value on None"); } @Override public boolean isNone() { return true; } @Override public boolean isSome() { return false; } // @Override @SuppressWarnings("unchecked") public Iterator<T> iterator() { return NONE_ITER; } } private static final class Some<T> extends Maybe<T> { private final T t; private Some(T t) { this.t = t; } @Override public T get() { return t; } @Override public boolean isNone() { return false; } @Override public boolean isSome() { return true; } // @Override public Iterator<T> iterator() { return new Iterator<T>() { private boolean done = false; // @Override public boolean hasNext() { return !done; } // @Override public T next() { if (done) throw new NoSuchElementException(); done = true; return t; } // @Override public void remove() { throw new UnsupportedOperationException(); } }; } } @SuppressWarnings("unchecked") private static final None NONE = new None(); @SuppressWarnings("unchecked") private static final Iterator NONE_ITER = new Iterator() { // @Override public boolean hasNext() { return false; } // @Override public Object next() { throw new NoSuchElementException(); } // @Override public void remove() { throw new UnsupportedOperationException(); } }; public static final <E> Maybe<E> first(E[] es) { if (es.length == 0) return none(); return maybe(es[0]); } public static final <E> Maybe<E> first(Iterable<E> es) { for (E e : es) return maybe(e); return none(); } public static final <E> Maybe<E> first(List<E> es) { if (es.isEmpty()) return none(); return maybe(es.get(0)); } public static final <E> Maybe<E> last(E[] es) { if (es.length == 0) return none(); return maybe(es[es.length - 1]); } public static final <E> Maybe<E> last(Iterable<E> es) { E $ = null; for (E e : es) $ = e; return maybe($); } public static final <E> Maybe<E> last(List<E> es) { if (es.isEmpty()) return none(); return maybe(es.get(es.size() - 1)); } public static <T> Maybe<T> maybe(Maybe<T> maybe) { return maybe; } public static <T> Maybe<T> maybe(T t) { return t == null ? Maybe.<T>none() : Maybe.<T>some(t); } @SuppressWarnings("unchecked") public static <T> Maybe<T> none() { return NONE; } public static <T> Maybe<T> some(T t) { return new Some<T>(t); } private Maybe() { // avoid more than two subclasses } public abstract T get(); public abstract boolean isNone(); public abstract boolean isSome(); }