The Java 8 lambda syntax and functional interfaces have been a productivity boost for Java developers. But there is one drawback to functional interfaces. None of the them as currently defined in Java 8 declare any checked exceptions. This leaves the developer at odds on how best to handle checked exceptions. This post will present one option for handling checked exceptions in functional interfaces. We will use use the
Function in our example, but the pattern should apply to any of the functional interfaces.
Example of a Function with a Checked Exception
Here’s an example from a recent side-project using a
Function to open a directory in Lucene. As expected, opening a directory for writing/searching throws an
1 2 3 4 5 6 7
While this example works, it feels a bit awkward with the
try/catch block. It’s adding to the boilerplate that functional interfaces are trying to reduce. Also, we are just re-throwing the exception up the call stack. If this how we are going to handle exceptions, is there something we can do to make our code a little bit cleaner?
A Proposed Solution
The solution is straight forward. We are going to extend the
Function interface and add a method called
throwsApply method declares a throws clause of type
Exception. Then we override the
applymethod as a default method to handle the call to
throwsApply in a try/catch block. Any exceptions caught are re-thrown as
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Here we are doing exactly what we did in the previous example, but from within a functional interface. Now we can rework our previous example to this: (made even more concise by using a method handle)
Composing Functions that have Checked Exceptions
Now we have another issue to tackle. How do we compose two or more functions involving checked exceptions? The solution is to create two new default methods. We create
compose methods allowing us to compose
ThrowingFunction objects into one. (These methods have the same name as the default methods in the
Function interface for consistency.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
The code for the
compose is the same found in the original
Function interface. We’ve just added try/catch blocks and re-throw any
Exception as a
RuntimeException. Now we are handling exceptions in the same manner as before with the added benefit of our code being a little more concise.
This approach is not without its drawbacks.
Brian Goetz spoke about this in his blog post ‘Exception transparency in Java’. Also, when composing functions we must use the type of
ThrowingFunction for all parts. This is regardless if some of the functions don’t throw any checked exceptions. There is one exception, the last function added could be of type
The purpose of this post is not to say this the best approach for handling checked exceptions, but to present one option. In a future post we will look at other ways of handling checked exceptions in functional interfaces.