Exceptional: an Optional for Exceptions?
Join the DZone community and get the full member experience.
Join For FreeI'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); } }
Opinions expressed by DZone contributors are their own.
Trending
-
Effortlessly Streamlining Test-Driven Development and CI Testing for Kafka Developers
-
Is Podman a Drop-in Replacement for Docker?
-
The SPACE Framework for Developer Productivity
-
Unlocking the Power of AIOps: Enhancing DevOps With Intelligent Automation for Optimized IT Operations
Comments