Sunday, February 14, 2016

Java generic exception specifiers

I'm not sure it's widely appreciated that throws clauses can take generic parameters, just as return type or arguments. You can leverage this to improve your error handling. Note the helpful type inference provided by the compiler:

public final class ErrorHandlingMain {
    public static void main(final String... args) {
        final Result<String, RuntimeException> fooResult
                = success("foo");
        final Result<String, Exception> barResult
                = failure(new IOException("bar")); // Note #1

        out.println(fooResult.get());  // Note #2
        out.println(get(fooResult));   // Note #3
        try {
            out.println(barResult.get());  // Note #4
        } catch (final Exception e) {
            out.println(e);
        }
        try {
            out.println(get(barResult));
        } catch (final Exception e) {
            out.println(e);
        }
    }

    public static <T, E extends Exception>
    T get(final Result<T, E> result)
            throws E {
        return result.get();
    }

    @FunctionalInterface
    public interface Result<T, E extends Exception> {
        T get()
                throws E;

        static <T> Result<T, RuntimeException>
        success(final T value) {
            return () -> value;
        }

        static <T, E extends Exception> Result<T, E>
        failure(
                final E exception) {
            return () -> {
                throw exception;
            };
        }
    }
}

(Unusual formatting to help with screen width.)

  1. Note type widening from IOException to Exception. Reversing those types won't compile.
  2. Compiler sees RuntimeException, does not require try/catch.
  3. Likewise for static methods.
  4. Compiler sees Exception, requires try/catch.

No comments: