Lambda Expressions and Exception Handling

This short article will explore a few options to handle checked and unchecked exceptions in lambda expressions.

With the introduction of lambda expressions in Java8, significant support was added for functional-style programming, thus promoting a more concise coding style.

But even then, the lambda expressions lag basic exception handling features and at times lead to more verbose code on the contrary.

This short article will explore a few options to wrap lambda expressions inside exception handlers for checked and unchecked exceptions.

Exception Prone Action

The first task for us is to define an exception-prone action that can terminate abruptly due to a checked or unchecked exception. As we anyways have to devise a solution for lambda expressions, this action has to be a FunctionalInterface that we can use to map lambda expressions.

Depending on our requirement, we can have actions that map lambda expressions with or without a return value.

Action with return value

The following is an example of a FunctionalInterface, to which lambda expression - producing some results on completion, can map to:

// ExceptionProneAction with return value
@FunctionalInterface
public interface ExceptionProneAction<T> {
    T doAction() throws Exception;
}

Here the generic type <T> refers to the result type that the lambda expression will produce after completion.

Action without return value

On similar lines, we can write another interface to which lambda expressions without a return value can map:

// ExceptionProneAction with NO return value
@FunctionalInterface
public interface ExceptionProneActionWithNoResult{
    void doAction() throws Exception;
}

Utility Methods to wrap exceptions

Once our actions are ready, we need to write utility methods that accept lambda expressions (mapped to those actions) and trigger the corresponding operations. The idea is to re-use these actions and utility methods wherever we need to handle exceptions while working with lambda expressions:

Unchecked with no return value

The following method handles an action capable of throwing a checked exception. The exception can be either only logged or processed by a concrete exception handler passed as an argument:

// Wrapping exception prone action with no return value
public static void uncheckedNoReturnValue(final ExceptionProneActionWithNoResult action, Consumer<Exception> handler){
    try{
        action.doAction();
    }catch(Exception exception){
        if(null != handler){
            handler.accept(exception);
        }
        logger.error("exception logged with some relevant message", exception);
    }
}

To use the action and utility method, we can write a wrapped lambda expression as follows:

// Example: wrap a lambda with **no return** value and no exception handler
uncheckedNoReturnValue(() -> Files.copy(Paths.get("from_path"),
                Paths.get("to_path"), StandardCopyOption.REPLACE_EXISTING), null);

Unchecked with return value

The only difference between the two versions is related to the underlying action to which lambda expression maps. For example, consider the following utility method that we can use to wrap lambda expressions with some return value:

// Wrapping exception prone action with **return** value
public static <T> T void uncheckedWithReturnValue(final ExceptionProneAction<T> action, Consumer<Exception> handler){
    T results = null;
    try{
        result = action.doAction();
    }catch(Exception exception){
        if(null != handler){
            handler.accept(exception);
        }
        logger.error("exception logged with some relevant message", exception);
    }
    return results;
}

The following snippet shows how we can use the utility method to wrap a lambda expression with a return value having the potential to throw an exception:

// Example: wrap a lambda with **return** value
Integer result = uncheckedWithReturnValue(days -> Integer.parseInt(days), 
            ex -> logger.error("this is a demo exception handler", ex));

Re-throwing exceptions

To further increase the available options, we can write additional utility methods that, instead of handling the exceptions themselves, throw those back to the calling code after wrapping the exceptions in some generic or application-specific exception that the client can handle.

The actions and utility methods mentioned above reduce the boilerplate code associated with traditional exception handling(via try-catch blocks). Moreover, it is now limited to only the static utility methods.  This way, we can write clean code which is more readable and is more aligned with the functional code style.  


That is all for this post. If you want to share any feedback, please drop me an email, or contact me on any social platforms. I’ll try to respond at the earliest. Also, please consider subscribing for regular updates.

Be notified of new posts. Subscribe to the RSS feed.