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(); } } });
A lambda expression cannot throw any checked exception until its corresponding functional interface declares a throws clause. An exception thrown by any lambda expression can be of the same type or sub-type of the exception declared in the throws clause of its functional interface.
A lambda expression body can't throw any exceptions that haven't specified in a functional interface. If the lambda expression can throw an exception then the "throws" clause of a functional interface must declare the same exception or one of its subtype.
The most straightforward way would be to use a try-catch block, wrap the checked exception into an unchecked exception and rethrow it: List<Integer> integers = Arrays. asList(3, 9, 7, 0, 10, 20); integers. forEach(i -> { try { writeToFile(i); } catch (IOException e) { throw new RuntimeException(e); } });
Fortunately, you can assign lambda expressions to variables and reuse them, as you would with objects.
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(); });
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With