Why can't I throw an exception in a Java 8 lambda expression? [duplicate]

I upgraded to Java 8 and tried to replace a simple iteration through a Map with a new lamdba expression. The loop searches for null values and throws an exception if one is found. The old Java 7 code looks like this:

for (Map.Entry<String, String> entry : myMap.entrySet()) {
    if(entry.getValue() == null) {
        throw new MyException("Key '" + entry.getKey() + "' not found!");
    }
}

And my attempt to convert this to Java 8 looks like this:

myMap.forEach((k,v) -> {
    if(v == null) {
        // OK
        System.out.println("Key '" + k+ "' not found!");

        // NOK! Unhandled exception type!
        throw new MyException("Key '" + k + "' not found!");
    }
});

Can anyone explain why the throw statement not allowed here and how this could be corrected?

Eclipse's quick-fix suggestion does not look right to me... it simply surrounds the throw statement with a try-catch block:

myMap.forEach((k,v) -> {
    if(v == null) {
        try {
            throw new MyException("Key '" + k + "' not found!");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
});

You are not allowed to throw checked exceptions because the accept(T t, U u) method in the java.util.function.BiConsumer<T, U> interface doesn't declare any exceptions in its throws clause. And, as you know, Map#forEach takes such a type.

public interface Map<K, V> {

    default void forEach(BiConsumer<? super K, ? super V> action) { ... }

}                        |
                         |
                         V
@FunctionalInterface
public interface BiConsumer<T, U> {

    void accept(T t, U u); // <-- does throw nothing

}

That is true when we are talking about checked exceptions. But you still can throw an unchecked exception (e.g. a java.lang.IllegalArgumentException):

new HashMap<String, String>()
    .forEach((a, b) -> { throw new IllegalArgumentException(); });

You can throw exceptions in lambdas.

A lambda is allowed to throw the same exceptions as the functional interface implemented by the lambda.

If the method of the functional interface doesn't have a throws clause, the lambda can't throw CheckedExceptions. (You still can throw RuntimeExceptions).


In your particular case, Map.forEach uses a BiConsumer as a parameter, BiConsumer is defined as:

public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

A lambda expression for this functional interface can't throw CheckedExceptions.


The methods in the functional interfaces defined in java.util.function package don't throw exceptions, but you can use other functional interfaces or create your own to be able to throw exceptions, i.e. given this interface:

public interface MyFunctionalInterface<T> {
    void accept(T t) throws Exception;
}

The following code would be legal:

MyFunctionalInterface<String> f = (s)->throw new Exception("Exception thrown in lambda");