Java 8: Better exceptions

Prompted by JUnit GitHub issue #706, Lukas Eder ponders a new method proposal.
This post was originally published over at jooq.org as part of a special series focusing on all things Java 8, including how take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.
I had the idea when I stumbled upon JUnit GitHub issue #706, which is about a new method proposal:
ExpectedException#expect(Throwable, Callable)
One suggestion was to create an interceptor for exceptions like this.
assertEquals(Exception.class,
thrown(() -> foo()).getClass());
assertEquals("yikes!",
thrown(() -> foo()).getMessage());
On the other hand, why not just add something completely new along the lines of this?
// This is needed to allow for throwing Throwables
// from lambda expressions
@FunctionalInterface
interface ThrowableRunnable {
void run() throws Throwable;
}
// Assert a Throwable type
static void assertThrows(
Class<? extends Throwable> throwable,
ThrowableRunnable runnable
) {
assertThrows(throwable, runnable, t -> {});
}
// Assert a Throwable type and implement more
// assertions in a consumer
static void assertThrows(
Class<? extends Throwable> throwable,
ThrowableRunnable runnable,
Consumer<Throwable> exceptionConsumer
) {
boolean fail = false;
try {
runnable.run();
fail = true;
}
catch (Throwable t) {
if (!throwable.isInstance(t))
Assert.fail("Bad exception type");
exceptionConsumer.accept(t);
}
if (fail)
Assert.fail("No exception was thrown");
}
So the above methods both assert that a given throwable is
thrown from a given runnable
– ThrowableRunnable
to be precise, because
most functional interfaces, unfortunately, don’t allow for throwing
checked exceptions. See
this article for details.
We’re now using the above hypothetical JUnit API as such:
assertThrows(Exception.class,
() -> { throw new Exception(); });
assertThrows(Exception.class,
() -> { throw new Exception("Message"); },
e -> assertEquals("Message", e.getMessage()));
In fact, we could even go further and declare an exception swallowing helper method like this:
// This essentially swallows exceptions
static void withExceptions(
ThrowableRunnable runnable
) {
withExceptions(runnable, t -> {});
}
// This delegates exception handling to a consumer
static void withExceptions(
ThrowableRunnable runnable,
Consumer<Throwable> exceptionConsumer
) {
try {
runnable.run();
}
catch (Throwable t) {
exceptionConsumer.accept(t);
}
}
This is useful to swallow all sorts of exceptions. The following two idioms are thus equivalent:
try {
// This will fail
assertThrows(SQLException.class, () -> {
throw new Exception();
});
}
catch (Throwable t) {
t.printStackTrace();
}
withExceptions(
// This will fail
() -> assertThrows(SQLException.class, () -> {
throw new Exception();
}),
t -> t.printStackTrace()
);
Obviuously, these idioms aren’t necessarily more useful than an
actual try .. catch .. finally
block,
specifically also because it does not support proper typing of
exceptions (at least not in this example), nor does it support the
try-with-resources statement.
Nonetheless, such utility methods will come in handy every now and then.