为什么我不能在Java 8 lambda表达式中抛出异常?
问题描述:
我升级到Java 8并试图用新的lamdba表达式替换通过Map的简单迭代。循环搜索空值并在发现异常时引发异常。旧的Java 7的代码如下所示:为什么我不能在Java 8 lambda表达式中抛出异常?
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();
}
}
});
答
你不允许抛出checked异常,因为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(); });
答
可以在lambda表达式抛出异常。
允许lambda抛出与lambda实现的函数接口相同的异常。
如果函数接口的方法没有throws子句,则lambda不能抛出CheckedExceptions。 (你仍然可以抛出RuntimeExceptions)。
在您的特定情况下,Map.forEach
使用BiConsumer
作为参数,BiConsumer定义为:
public interface BiConsumer<T, U> {
void accept(T t, U u);
}
此功能接口不能扔CheckedExceptions lambda表达式。
在功能界面中java.util.function
包中定义的方法不抛出异常,但你可以使用其他功能接口或者创建自己能够抛出异常,即给该接口:
public interface MyFunctionalInterface<T> {
void accept(T t) throws Exception;
}
下面的代码是合法的:
MyFunctionalInterface<String> f = (s)->throw new Exception("Exception thrown in lambda");
有一个非常简单的答案:lambda表达式是实现一个功能界面的简洁方式。就像其他任何实现接口的方式一样,您必须遵守接口协定 - 这意味着您只能抛出目标类型允许的异常。这里没有魔法。 –