Exception Handling in Lambda Expression

Java 8 introduces a lot of new API to ease the Functional Programming paradigm for smaller, concise, smaller and reduce boilerplate code. Functional Programming doesn't handle the Exceptions very well.

Java 7 or prior, Exceptions were handled using try-catch block, same holds true for Java 8 as well. since lambda expressions are closely bound to its functional interface, hence it cannot throw an exception unless the corresponding functional interface declares the same.

Unchecked Exception

An Unchecked Exception cannot catch by the compiler and occurs at run time, hence is also called Runtime Exception. Since the compiler cannot capture the Runtime Exception, it will not give any compilation error but throw an Exception at Runtime.

Runtime Exceptions should not be handled or declared

Runtime Exceptions usually happen due to programming errors, poor coding, unseen bug or wrong logic, and such Exceptions should not be caught but let the Exception occur and deal with changes in logic or programming.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;

class Apple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
        list.stream().forEach(apple -> {
            System.out.println(apple);
            throw new RuntimeException("Unchecked Exception in Lambda");
        });
    }
}

so far the only way to handle the Exception is to add a try-catch block

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;

class Apple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
        list.stream().forEach(apple -> {
            try {
                System.out.println(apple);
                throw new RuntimeException("Unchecked Exception in Lambda");
            } catch (RuntimeException exception) {
                System.out.println(exception.getMessage());
            }
        });
    }
}

Checked Exception

A Checked Exception is caught by the compiler at Compile Time only, hence called Compile time Exception. Checked Exception can be handled either by re-throwing the same Exception or with a try-catch block.

Checked Exception in Lambda Expression can be handled in 3 ways.

  • Add a try-catch block inside the lambda

  • Extract Exception block to a wrapper method

  • Create a Custom Functional Interface which throws an Exception

  • Create a Custom Functional Interface which extends Functional Interface and overrides existing method 

try-catch block in lambda statement

Lambda Expression allows complete body as well, so the try-catch block can be incorporated inside the Lambda Expression. It's the easiest way to handle exceptions but will lose the conciseness and readability of the code.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;

class Apple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
        list.stream().forEach(apple -> {
            try {
                System.out.println(apple);
                throw new Exception("Checked Exception in Lambda");
            } catch (Exception e) {
                System.out.println(exception.getMessage());
            }
        });
    }
}

Extract Exception block to a wrapper method

Lambda Expression is known for its conciseness, shorter code, less boilerplate code, and readability. adding a try-catch block inside lambda will lose all the above characteristics, a simpler solution would be to extract the exception block to another method and call the method from Lambda Expression.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

class Apple {
    public static void main(String[] args) {
        Consumer<String> consumer = (apple) -> {
            try {
                System.out.println(apple);
                throw new RuntimeException("Checked Exception in Lambda");
            } catch (RuntimeException exception) {
                System.out.println(exception.getMessage());
            }
        };

        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
        /*  Exception block to a wrapper method using consumer  */
        list.stream().forEach(consumer);
        
        /*  Exception block to a wrapper method using another method  */
        list.stream().forEach(Apple::consumer);
    }

    public static void consumer(String apple) {
        try {
            System.out.println(apple);
            throw new RuntimeException("Checked Exception in Lambda");
        } catch (RuntimeException exception) {
            System.out.println(exception.getMessage());
        }
    }
}

Create a Custom Functional Interface which throws Exception

Extracting the Exception block to another wrapper method will make the lambda code concise but will add the overhead of creating method objects and executing the try-catch block for each call.

the best solution is to create a custom functional interface whose abstract method throws the Exception and another static method which accepts the customer functional interface, will catch the Exception

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

import static org.wesome.java8.ThrowingConsumer.wrapper;

@FunctionalInterface
interface ThrowingConsumer<T, E extends Exception> {
    void accept(T t) throws E;

    static <T> Consumer<T> wrapper(ThrowingConsumer<T, Exception> t) {
        return arg -> {
            try {
                t.accept(arg);
            } catch (Exception e) {
                System.out.println(exception.getMessage());
            }
        };
    }
}
class Apple {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
        list.forEach(wrapper(System.out::println));
    }
}

Create a Custom Functional Interface which extends the Functional Interface and overrides the existing method 

some Lambda functions have predefined input type and any Custom Functional Interface will not be accepted, in these cases, create a Custom Functional Interface, extends the Functional Interface which is required, and override the default method.

package org.wesome.java8;

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

@FunctionalInterface
interface CustomFunctionalInterface<T> extends Consumer<T> {
    @Override
    default void accept(T elem) {
        try {
            acceptThrows(elem);
        } catch (Exception exception) {
            System.out.println(exception.getMessage());
        }
    }

    void acceptThrows(T elem) throws Exception;
}

class Apple {
    public static void main(String[] args) {

        CustomFunctionalInterface<String> consumer = (apple) -> {
            System.out.println(apple);
            throw new Exception("Checked Exception in Lambda");
        };

        List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");

        list.forEach(consumer);
    }
}

 

follow us on