Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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();         }     } }); 
like image 487
dokaspar Avatar asked Jun 09 '16 13:06

dokaspar


People also ask

Can we throw exception from lambda expression in Java?

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.

Can you throw any exception inside a lambda expression body?

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.

How do you handle exceptions in lambda expression?

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

Can lambda expression be reused?

Fortunately, you can assign lambda expressions to variables and reuse them, as you would with objects.


1 Answers

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(); }); 
like image 168
Andrew Tobilko Avatar answered Sep 24 '22 19:09

Andrew Tobilko