Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Exceptional: an Optional for Exceptions?

DZone's Guide to

Exceptional: an Optional for Exceptions?

· Java Zone ·
Free Resource

Get the Edge with a Professional Java IDE. 30-day free trial.

I've been exploring Java 8 lately and started to poke at Optional. After a while it occurred to me that there is more than one reason for a method to fail to return a valid value. When an exception is thrown no valid value is returned from a method. What would a Optional for those situations look like? Would it be useful? Here is my first try. 

I'll show you the tests first to explain how it might work.

This shows that if the value is present then you should have no problem getting it.

@Test
public void testSimpleValuePresent() {
Exceptional<String> value = Exceptional.of("Value");
assertTrue(value.isPresent());
assertEquals("Value", value.get());
}

If the value exists you can also pass a Consumer to it. 

  @Test
public void testIfPresentWithValue() {
Exceptional<String> value = Exceptional.of("Value");




StringBuilder result = new StringBuilder(); 
value.ifPresent( val -> result.append(val));




assertEquals("Value", result.toString());
}

If the value does not exists and you pass a Consumer to it, the consumer will not execute.

  public void testIfPresentWithoutValue() {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException(
"12345"));




StringBuilder result = new StringBuilder(); 
value.ifPresent( val -> result.append(val));




assertEquals("", result.toString());
}

You can also test to see if an Exception is present and take actions accordingly. 

  @Test(expected=RuntimeException.class)
public void testExceptionIsPresent() {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException( "12345"));
assertFalse(value.isPresent());
value.get();
}




@Test
public void testIfAnyExceptionIsPresent() {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException( "12345"));
StringBuilder result = new StringBuilder(); 
value.ifExceptionPresent(ex -> result.append(ex.getMessage()));
assertEquals("12345", result.toString());
}




@Test
public void testIfOneTypeOfExceptionIsNotPresent() {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException( "12345"));




StringBuilder result = new StringBuilder(); 
value.ifExceptionPresent(IOException.class, ex -> result.append(
ex.getMessage()));




assertEquals("", result.toString());
}




@Test
public void testIfOneTypeOfExceptionIsPresent() {
Exceptional<String> value = Exceptional.of(new IOException("12345"));
StringBuilder result = new StringBuilder(); 
value.ifExceptionPresent(IOException.class, ex -> result.append(
ex.getMessage()));
assertEquals("12345", result.toString());
}




@Test
public void testIfExceptionIsNotPresent() {
Exceptional<String> value = Exceptional.of("Value");
StringBuilder result = new StringBuilder(); 
value.ifExceptionPresent(ex -> result.append(ex.getMessage()));
assertEquals("", result.toString());
}

Now, if you don't care what happened you can just use orElse to return a default value. 

  @Test
public void testOrElseValue() {
Exceptional<String> value = Exceptional.of("Value");  
assertEquals("Value", value.orElse("Alternative Value"));
}
@Test
public void testOrElseAlternativeValue() {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException("12345"));
assertEquals("Alternative Value", value.orElse("Alternative Value"));
}

Finally it has the ability to rethrow the exception if necessary. 

@Test(expected=IllegalArgumentException.class)
public void testRethrow() throws Exception {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException("12345"));
value.rethrow();
}




@Test
public void testRethrowWithoutException() throws Exception {
Exceptional<String> value = Exceptional.of("Value");
value.rethrow();
}




@Test(expected=RuntimeException.class)
public void testRethrowRuntime() throws Exception {
Exceptional<String> value = Exceptional.of(new IllegalArgumentException("12345"));
value.rethrowRuntime();
}








@Test
public void testRethrowRuntimeWithoutException() throws Exception {
Exceptional<String> value = Exceptional.of("Value");
value.rethrowRuntime();
}

So that's the basic idea. Good, Bad? Let me know what you think.

package com.collinfagan.exceptional;




import java.util.Objects;
import java.util.function.Consumer;




/**
* Class to deal with exceptions in the style of optional.
* 
* @author Collin Fagan
*
* @param <T>
* - the type this wraps
*/
public class Exceptional<T> {




private static final Exceptional<?> EMPTY = new Exceptional<>();




private final T value;
private final Exception exception;




private Exceptional() {
this.value = null;
this.exception = null;
}




public void rethrowRuntime() {
if (exception != null) {
throw new RuntimeException(exception);
}
}




public void rethrow() throws Exception {
if (exception != null) {
throw exception;
}
}




public Exception getException() {
return exception;
}




public static <T> Exceptional<T> empty() {
@SuppressWarnings("unchecked")
Exceptional<T> t = (Exceptional<T>) EMPTY;
return t;
}




private Exceptional(T value) {
this.value = Objects.requireNonNull(value);
this.exception = null;
}




private Exceptional(Exception exception) {
this.exception = Objects.requireNonNull(exception);
value = null;
}




public static <T> Exceptional<T> of(T value) {
return new Exceptional<>(value);
}




public static <T> Exceptional<T> of(Exception exception) {
return new Exceptional<>(exception);
}




public static <T> Exceptional<T> ofException(Exception exception) {
return exception == null ? empty() : of(exception);
}








public T get() {
if (exception != null) {
throw new RuntimeException(exception);
}
return value;
}




public boolean isPresent() {
return exception == null;
}




public void ifPresent(Consumer<? super T> consumer) {
if (exception == null) {
consumer.accept(value);
}
}




public void ifExceptionPresent(Consumer<? super Exception> consumer) {
if (exception != null)
consumer.accept(exception);
}




public void ifExceptionPresent(Class<? extends Exception> targetType,
Consumer<? super Exception> consumer) {
if (exception != null
&& targetType.isAssignableFrom(exception.getClass())) {
consumer.accept(exception);
}
}




public T orElse(T other) {
return exception == null ? value : other;
}




@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}




if (!(obj instanceof Exceptional)) {
return false;
}




Exceptional<?> other = (Exceptional<?>) obj;
return Objects.equals(value, other.value);
}




@Override
public int hashCode() {
return Objects.hashCode(value);
}




@Override
public String toString() {
return exception == null ? String.format("Exceptional[%s]", value)
: String.format("Exceptional[%s]", exception);
}




}

Get the Java IDE that understands code & makes developing enjoyable. Level up your code with IntelliJ IDEA. Download the free trial.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}