Почему я не могу исключить исключение в выражении лямбда Java 8?
Я обновился до Java 8 и попытался заменить простую итерацию с помощью Map с новым выражением lamdba. Цикл ищет нулевые значения и генерирует исключение, если оно найдено. Старый код Java 7 выглядит следующим образом:
for (Map.Entry<String, String> entry : myMap.entrySet()) {
if(entry.getValue() == null) {
throw new MyException("Key '" + entry.getKey() + "' not found!");
}
}
И моя попытка конвертировать это в Java 8 выглядит так:
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!");
}
});
Может ли кто-нибудь объяснить, почему оператор throw
здесь не разрешен и как это можно исправить?
Предложение быстрого исправления Eclipse не подходит для меня... оно просто окружает оператор throw
блоком try-catch
:
myMap.forEach((k,v) -> {
if(v == null) {
try {
throw new MyException("Key '" + k + "' not found!");
}
catch (Exception e) {
e.printStackTrace();
}
}
});
Ответы
Ответ 1
Вам запрещено выдавать проверенные исключения, потому что метод void accept(T t, U u)
в интерфейсе BiConsumer<T, U>
не генерирует никаких исключений. И, как вы знаете, forEach
принимает такой тип.
public void forEach(BiConsumer<? super K, ? super V> action) { ... }
|
V
@FunctionalInterface
public interface BiConsumer<T, U> {
void accept(T t, U u); // <-- does throw nothing
}
Это правда, когда мы говорим об отмеченных исключениях. Но вам разрешено исключать неконтролируемое исключение, например, IllegalArgumentException
:
new HashMap<String, String>()
.forEach((a, b) -> { throw new IllegalArgumentException(); });
Ответ 2
Вы можете исключать исключения из lambdas.
Лямбде разрешено использовать те же исключения, что и функциональный интерфейс, реализованный лямбдой.
Если у метода функционального интерфейса нет предложения throws, лямбда не может выбрасывать CheckedExceptions. (Вы все еще можете использовать RuntimeExceptions).
В вашем конкретном случае Map.forEach
использует параметр BiConsumer
в качестве параметра, BiConsumer определяется как:
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
Лямбда-выражение для этого функционального интерфейса не может вызывать CheckedExceptions.
Методы функциональных интерфейсов, определенные в пакете java.util.function
, не генерируют исключений, но вы можете использовать другие функциональные интерфейсы или создавать свои собственные, чтобы иметь возможность генерировать исключения, т.е. с учетом этого интерфейса:
public interface MyFunctionalInterface<T> {
void accept(T t) throws Exception;
}
Следующий код будет легальным:
MyFunctionalInterface<String> f = (s)->throw new Exception("Exception thrown in lambda");