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<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> Class.forName(className))
.collect(Collectors.toList());
return classes;
}
This code does not compile, since the Class.forName()
method above throws ClassNotFoundException
, which is checked.
Please note I do NOT want to wrap the checked exception inside a runtime exception and throw the wrapped unchecked exception instead. I want to throw the checked exception itself, and without adding ugly try
/catches
to the stream.
Probably, a better and more functional way is to wrap exceptions and propagate them further in the stream. Take a look at the Try type of Vavr for example.
Example:
OR
2nd implementation avoids wrapping the exception in a
RuntimeException
.throwUnchecked
works because almost always all generic exceptions are treated as unchecked in java.The only built-in way of handling checked exceptions that can be thrown by a
map
operation is to encapsulate them within aCompletableFuture
. (AnOptional
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:This produces the following output:
The
applyOrDie
method takes aFunction
that throws an exception, and converts it into aFunction
that returns an already-completedCompletableFuture
-- 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 aStream<CompletableFuture<T>>
instead of just aStream<T>
.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" theCompletableFuture
--cfCollector()
lets us do that using asupplier
,accumulator
,combiner
, andfinisher
that don't need to know anything at all aboutCompletableFuture
.The helper methods can be found on GitHub in my
MonadUtils
class, which is very much still a work in progress.I think this approach is the right one:
Wrapping the checked exception inside the
Callable
in aUndeclaredThrowableException
(that’s the use case for this exception) and unwrapping it outside.Yes, I find it ugly, and I would advise against using lambdas in this case and just fall back to a good old loop, unless you are working with a parallel stream and paralellization brings an objective benefit that justifies the unreadability of the code.
As many others have pointed out, there are solutions to this situation, and I hope one of them will make it into a future version of Java.
This
LambdaExceptionUtil
helper class lets you use any checked exceptions in Java streams, like this:Note
Class::forName
throwsClassNotFoundException
, which is checked. The stream itself also throwsClassNotFoundException
, and NOT some wrapping unchecked exception.Many other examples on how to use it (after statically importing
LambdaExceptionUtil
):NOTE 1: The
rethrow
methods of theLambdaExceptionUtil
class above may be used without fear, and are OK to use in any situation. A big thanks to user @PaoloC who helped solve the last problem: Now the compiler will ask you to add throw clauses and everything's as if you could throw checked exceptions natively on Java 8 streams.NOTE 2: The
uncheck
methods of theLambdaExceptionUtil
class above are bonus methods, and may be safely removed them from the class if you don't want to use them. If you do used them, do it with care, and not before understanding the following use cases, advantages/disadvantages and limitations:• You may use the
uncheck
methods if you are calling a method which literally can never throw the exception that it declares. For example: new String(byteArr, "UTF-8") throws UnsupportedEncodingException, but UTF-8 is guaranteed by the Java spec to always be present. Here, the throws declaration is a nuisance and any solution to silence it with minimal boilerplate is welcome:String text = uncheck(() -> new String(byteArr, "UTF-8"));
• You may use the
uncheck
methods if you are implementing a strict interface where you don't have the option for adding a throws declaration, and yet throwing an exception is entirely appropriate. Wrapping an exception just to gain the privilege of throwing it results in a stacktrace with spurious exceptions which contribute no information about what actually went wrong. A good example is Runnable.run(), which does not throw any checked exceptions.• In any case, if you decide to use the
uncheck
methods, be aware of these 2 consequences of throwing CHECKED exceptions without a throws clause: 1) The calling-code won't be able to catch it by name (if you try, the compiler will say: Exception is never thrown in body of corresponding try statement). It will bubble and probably be caught in the main program loop by some "catch Exception" or "catch Throwable", which may be what you want anyway. 2) It violates the principle of least surprise: it will no longer be enough to catchRuntimeException
to be able to guarantee catching all possible exceptions. For this reason, I believe this should not be done in framework code, but only in business code that you completely control.I use this kind of wrapping exception:
It will require handling these exceptions statically:
Though exception will be anyway re-thrown during first
rethrow()
call (oh, Java generics...), this way allows to get a strict statical definition of possible exceptions (requires to declare them inthrows
). And noinstanceof
or something is needed.TL;DR Just use Lombok's
@SneakyThrows
.Christian Hujer has already explained in detail why throwing checked exceptions from a stream is, strictly speaking, not possible due to Java's limitations.
Some other answers have explained tricks to get around the limitations of the language but still being able to fulfil the requirement of throwing "the checked exception itself, and without adding ugly try/catches to the stream", some of them requiring tens of additional lines of boilerplate.
I am going to highlight another option for doing this that IMHO is far cleaner than all the others: Lombok's
@SneakyThrows
. It has been mentioned in passing by other answers but was a bit buried under a lot of unnecessary detail.The resulting code is as simple as:
We just needed one
Extract Method
refactoring (done by the IDE) and one additional line for@SneakyThrows
. The annotation takes care of adding all the boilerplate to make sure that you can throw your checked exception without wrapping it in aRuntimeException
and without needing to declare it explicitly.