Skip to content

Instantly share code, notes, and snippets.

@msfroh
Created April 18, 2012 06:21
Show Gist options
  • Save msfroh/2411438 to your computer and use it in GitHub Desktop.
Save msfroh/2411438 to your computer and use it in GitHub Desktop.
Option
public abstract class Option<T> {
public abstract T get();
public abstract boolean isDefined();
// Factory method to return the singleton None instance
@SuppressWarnings({"unchecked"})
public static <T> Option<T> none() {
return NONE;
}
// Factory method to return a non-empty Some instance
public static <T> Option<T> some(final T value) {
return new Some<T>(value);
}
private static None NONE = new None();
private static class None extends Option {
// None has no element to return from get()
@Override
public Object get() {
throw new NoSuchElementException("get() called on None");
}
// None is never defined
@Override
public boolean isDefined() {
return false;
}
// We'll override toString() to make tests/debugging clearer
@Override
public String toString() {
return "None";
}
}
private static class Some<T> extends Option<T> {
private final T value;
// Some wraps an object value. It is up to the caller
// of the some() factory method above to ensure that
// value is not null.
public Some(final T value) {
this.value = value;
}
// Return the wrapped value
@Override
public T get() {
return value;
}
// Some is always defined
@Override
public boolean isDefined() {
return true;
}
// We'll override toString() to make tests/debugging clearer
@Override
public String toString() {
return "Some(" + value + ")";
}
// We didn't need to override equals() for None, since it is a
// singleton and referential equality is fine. Some needs to
// define equals() in terms of the contained value.
public boolean equals(Object other) {
return other instanceof Some && ((Some) other).value.equals(value);
}
}
}
public abstract class Option<T> implements Iterable<T> {
/* ... previous method and subclass definitions ... */
public final T getOrElse(T defaultVal) {
return isDefined() ? get() : defaultVal;
}
}
public abstract class Option<T> implements Iterable<T> {
/* ... previous method and subclass definitions ... */
@Override
public Iterator<T> iterator() {
return isDefined() ? Collections.singleton(get()).iterator() :
Collections.<T>emptySet().iterator();
}
}
public abstract class Option<T> implements Iterable<T> {
/* ... previous method and subclass definitions ... */
public static <T> Option<T> option(T value) {
if (value == null) {
return none();
}
return some(value);
}
}
public class OptionTest {
/* ... previous tests ... */
@Test
public void testGet() throws Exception {
Option<Integer> a = some(5);
Option<Integer> b = none();
assertEquals(5, a.get().intValue());
try {
b.get();
fail("Should have thrown exception");
} catch (NoSuchElementException e) {
// Exception should have been thrown
}
// Some should return its own value
assertEquals(5, a.getOrElse(1).intValue());
// None returns the given default value
assertEquals(1, b.getOrElse(1).intValue());
}
}
import static collections.Option.none;
import static collections.Option.some;
import static org.junit.Assert.*;
public class OptionTest {
@Test
public void testIterable() throws Exception {
// For Some, the body of the for loop will execute
Option<Integer> a = some(5);
boolean didRun = false;
for (Integer i : a) {
didRun = true;
assertEquals(Integer.valueOf(5), i);
}
assertTrue(didRun);
// For None, it does not execute
Option<Integer> b = none();
for (Integer i : b) {
fail("This should not execute");
}
}
}
/* ... previous imports ... */
import static collections.Option.option;
public class OptionTest {
/* ... previous tests ... */
@Test
public void testWrapNull() throws Exception {
Map<String, Integer> scoreMap = new HashMap<String, Integer>();
scoreMap.put("Michael", 42);
Option<Integer> a = option(scoreMap.get("Michael"));
assertTrue(a.isDefined());
Option<Integer> b = option(scoreMap.get("Bob"));
assertFalse(b.isDefined());
// Here is the ugly "traditional" Java way of dealing with Maps
Integer score = scoreMap.get("Michael");
if (score != null) {
System.out.println("Michael's score is " + score);
}
// This feels more elegant to me
for (Integer myScore : option(scoreMap.get("Michael"))) {
System.out.println("Michael's score is " + score);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment