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");