2016-06-09 34 views
16

我升级到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(); 
     } 
    } 
}); 
+12

有一个非常简单的答案:lambda表达式是实现一个功能界面的简洁方式。就像其他任何实现接口的方式一样,您必须遵守接口协定 - 这意味着您只能抛出目标类型允许的异常。这里没有魔法。 –

回答

29

你不允许抛出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(); }); 
+3

我非常同意这个答案。关于javadocs的令人困惑的事情是“由操作抛出的异常被转发给调用者”。作为一个方面说明,在lambda中抛出异常肯定是允许的,而不是在这种特殊情况下。 – micker

+0

我也经历过同样的事情。检查异常是不允许的。 – lwpro2

+0

@micker值得指出的是:该语句仅附加在'Iterable '接口的'forEach'默认成员方法中。主流/消费者'forEach'没有记录关于检查异常行为的任何信息(尽管可以从功能签名中得出一些结论)。 –

18

可以在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"); 
相关问题