How to handle exceptions properly within streams: the functional way

Laurent Thiebaud
The Startup
Published in
4 min readMay 10, 2020

--

By upgrading to Java 8 and using Streams, as many of my colleagues I was disappointed by exception handling. Look at this example:

In this case, we have an unhandled exception: java.lang.Exception error. After some tests, I understood the following:

  • From a stream processing, we can throw a RuntimeException. It is meant to be used if there is a real problem, the stream processing is stopped;
  • Or if we don’t want to stop the whole processing, we only need to throw a caught Exception. Then it has to be handled within the stream. In our example we would do:

I was not totally satisfied by this solution. The only advice I found by googling was to handle the exception within a method to make the code more readable.

Really, is there nothing better to do?

Moreover, it is not totally satisfying: imagine we are processing a request, we are iterating over a list of objects we get from a database, we need to report about success and errors in the response.

Then we will have to instantiate some mutable Lists, one for success, one for errors; after the forEach we would have to browse each list.

There are other way to do it. In any case the lambda function is supposed to be small, to do only one thing but not to handle exceptions. This is a bad design.

Later I discovered Scala and its Try (not try but Try). Back to Java, I used the Cyclops library and found an equivalent of the Try. I finally understood how we are supposed the handle exception in a functional way:

  • If there is some big issue in the processing (eg. a required server is down): then we need to stop the whole processing. We use a Runtime Exception.
  • If there is an issue with some data, then other data should still be processed, but bad data should thus be put apart. The functional way is to map each data to its result, so that we can process the results later. The result can be either a Success or an Error. That’s what the scala Try is made for.

Try is a generic abstract class, with 2 implementations: Success and Failure.

With a naive implementation of these objects, the code looks like this:

We still use a try / catch within the lambda, but we are now able to handle the results in a functional way: we have a Stream of Try, that we can filter to get the success and the failures and handle them outside of the lambda, as we need to.

The code still looks bad, because we need a proper and generic implementation of Try. As I said I used to find one in the Cyclops library, but at this time it was very verbose and with very few documentation (maybe today it is better).
A light implementation is lambdista (it has no dependencies). With it you can simply write:

The Try.apply catches the exception for you and instantiate the proper Try implementation, either a Success or a Failure. The Try can do more, like with Optional you can also get a default value in case of failure:

You can also recover failures:

Since Java 8 we often say that Java is functional. After learning scala I mostly feel like this is an emulation of functional programming. Stream is an API but Collections aren’t functional by design. The API is new and still not sufficient to develop in a really functional way. Do not hesitate to use some libraries to make it better, but in my experience be careful to verbose libraries, keep it simple.

The best thing you can do to learn functional programming is to learn a really functional language (like scala). This way I learned some best practices that I can now apply to Java.

Back to exceptions, let’s sum up a good practice: process your data, and then process the results. In this case the design is clear: you do one thing at a time and you respect the single responsibility principle. The Try object helps you to get a result that is either a success or a failure.

If you found this article useful, please clap or follow me, and share it!

--

--

Laurent Thiebaud
The Startup

Nature lover, father of young twins. Looking for elegant and beautiful code.