Skip to content

Instantly share code, notes, and snippets.

@jesperdj
Last active December 15, 2015 04:19
Show Gist options
  • Save jesperdj/5200617 to your computer and use it in GitHub Desktop.
Save jesperdj/5200617 to your computer and use it in GitHub Desktop.
An Option class in Java.
/*
* Copyright 2013 Jesper de Jong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.util.Iterator;
import java.util.NoSuchElementException;
public abstract class Option<T> implements Iterable<T> {
private static final class None<T> extends Option<T> {
@Override
public boolean isEmpty() {
return true;
}
@Override
public Option<T> or(Option<T> alternative) {
return alternative;
}
@Override
public T getOrDefault(T defaultValue) {
return defaultValue;
}
@Override
public T getOrNull() {
return null;
}
@Override
public T getOrThrow(String exceptionMessage) {
throw new IllegalStateException(exceptionMessage);
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return false;
}
@Override
public T next() {
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return obj instanceof None;
}
@Override
public String toString() {
return "None";
}
}
private static final class Some<T> extends Option<T> {
private final T value;
public Some(T value) {
this.value = value;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public Option<T> or(Option<T> alternative) {
return this;
}
@Override
public T getOrDefault(T defaultValue) {
return value;
}
@Override
public T getOrNull() {
return value;
}
@Override
public T getOrThrow(String exceptionMessage) {
return value;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private boolean hasNext = true;
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public T next() {
if (hasNext) {
hasNext = false;
return value;
} else {
throw new NoSuchElementException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((value == null) ? 0 : value.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
Some other = (Some) obj;
if (value == null) {
if (other.value != null)
return false;
} else if (!value.equals(other.value))
return false;
return true;
}
@Override
public String toString() {
return "Some(" + value + ")";
}
}
public static <T> Option<T> none() {
return new None<>();
}
public static <T> Option<T> some(T value) {
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
return new Some<>(value);
}
public static <T> Option<T> fromNullable(T value) {
return value != null ? new Some<>(value) : new None<T>();
}
public abstract boolean isEmpty();
public abstract Option<T> or(Option<T> alternative);
public abstract T getOrDefault(T defaultValue);
public abstract T getOrNull();
public abstract T getOrThrow(String exceptionMessage);
}
/*
* Copyright 2013 Jesper de Jong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import org.junit.Assert;
import org.junit.Test;
public final class OptionTest {
@Test(expected = IllegalArgumentException.class)
public void testSomeWithNull() {
Option.some(null);
}
@Test
public void testFromNullable() {
Assert.assertFalse(Option.fromNullable("test").isEmpty());
Assert.assertTrue(Option.fromNullable(null).isEmpty());
}
@Test
public void testIsEmpty() {
Assert.assertFalse(Option.some("test").isEmpty());
Assert.assertTrue(Option.none().isEmpty());
}
@Test
public void testOr() {
Option<String> none = Option.none();
Option<String> some1 = Option.some("test1");
Option<String> some2 = Option.some("test2");
Assert.assertSame(some1, none.or(some1));
Assert.assertSame(some1, some1.or(none));
Assert.assertSame(some1, some1.or(some2));
}
@Test
public void testGetOrDefault() {
Assert.assertEquals("default", Option.none().getOrDefault("default"));
Assert.assertEquals("test", Option.some("test").getOrDefault("default"));
}
@Test
public void testGetOrNull() {
Assert.assertEquals(null, Option.none().getOrNull());
Assert.assertEquals("test", Option.some("test").getOrNull());
}
@Test(expected = IllegalStateException.class)
public void testGetOrThrowNone() {
Option.none().getOrThrow("Exception");
}
@Test
public void testGetOrThrowSome() {
Assert.assertEquals("test", Option.some("test").getOrThrow("Exception"));
}
@Test
public void testIterableNone() {
for (Object value : Option.none()) {
Assert.fail();
}
}
@Test
public void testIterableSome() {
int count = 0;
for (String value : Option.some("test")) {
Assert.assertEquals("test", value);
++count;
}
Assert.assertEquals(1, count);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment