How can I throw CHECKED exceptions from inside Java 8 streams/lambdas?
In other words, I want to make code like this compile:
public List
The only built-in way of handling checked exceptions that can be thrown by a map
operation is to encapsulate them within a CompletableFuture. (An Optional is a simpler alternative if you don't need to preserve the exception.) These classes are intended to allow you to represent contingent operations in a functional way.
A couple of non-trivial helper methods are required, but you can arrive at code that's relatively concise, while still making it apparent that your stream's result is contingent on the map
operation having completed successfully. Here's what it looks like:
CompletableFuture>> classes =
Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double")
.map(MonadUtils.applyOrDie(Class::forName))
.map(cfc -> cfc.thenApply(Class::getSuperclass))
.collect(MonadUtils.cfCollector(ArrayList::new,
List::add,
(List> l1, List> l2) -> { l1.addAll(l2); return l1; },
x -> x));
classes.thenAccept(System.out::println)
.exceptionally(t -> { System.out.println("unable to get class: " + t); return null; });
This produces the following output:
[class java.lang.Object, class java.lang.Number, class java.lang.Number]
The applyOrDie method takes a Function
that throws an exception, and converts it into a Function
that returns an already-completed CompletableFuture
-- either completed normally with the original function's result, or completed exceptionally with the thrown exception.
The second map
operation illustrates that you've now got a Stream
instead of just a Stream
. CompletableFuture
takes care of only executing this operation if the upstream operation succeeded. The API makes this explict, but relatively painless.
Until you get to the collect
phase, that is. This is where we require a pretty significant helper method. We want to "lift" a normal collection operation (in this case, toList()
) "inside" the CompletableFuture
-- cfCollector() lets us do that using a supplier
, accumulator
, combiner
, and finisher
that don't need to know anything at all about CompletableFuture
.
The helper methods can be found on GitHub in my MonadUtils class, which is very much still a work in progress.