【问题标题】:Why can't I throw an exception in a Java 8 lambda expression? [duplicate]为什么我不能在 Java 8 lambda 表达式中抛出异常? [复制]
【发布时间】:2016-06-09 13:06:14
【问题描述】:

我升级到 Java 8 并尝试用新的 lamdba 表达式替换通过 Map 进行的简单迭代。循环搜索空值,如果找到则抛出异常。旧的 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 的快速修复建议在我看来不正确...它只是用 try-catch 块包围了 throw 语句:

myMap.forEach((k,v) -> {
    if(v == null) {
        try {
            throw new MyException("Key '" + k + "' not found!");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
});

【问题讨论】:

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

标签: java exception lambda java-8


【解决方案1】:

由于java.util.function.BiConsumer&lt;T, U&gt; 接口中的accept(T t, U u) 方法没有在其throws 子句中声明任何异常,因此不允许抛出已检查异常。而且,如您所知,Map#forEach 就是这样一种类型。

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

}

当我们谈论受检异常时确实如此。但是你仍然可以抛出未经检查的异常(例如java.lang.IllegalArgumentException):

new HashMap<String, String>()
    .forEach((a, b) -> { throw new IllegalArgumentException(); });

【讨论】:

  • 我非常同意这个答案。关于 javadocs 的令人困惑的事情是声明“操作引发的异常被转发给调用者。”。作为旁注,在 lambda 中抛出异常当然是允许的,只是在这种特殊情况下是不允许的。
  • @micker 可能值得指出:该语句仅附加到Iterable&lt;T&gt; 接口的forEach 默认成员方法。主要的 Stream/Consumer forEach 没有记录任何关于检查异常行为的内容(尽管可以从函数签名中得出一些结论)。
  • @andrew-tobilko,感谢您的回答!我想知道我们在 java Optional.ifPresentOrElse 中是否有类似的情况,我们也不能抛出任何已检查的异常。
【解决方案2】:

你可以在 lambdas 中抛出异常。

允许 lambda 抛出与 lambda 实现的函数式接口相同的异常。

如果函数式接口的方法没有 throws 子句,则 lambda 不能抛出 CheckedExceptions。 (您仍然可以抛出 RuntimeExceptions)。


在您的特定情况下,Map.forEach 使用 BiConsumer 作为参数,BiConsumer 定义为:

public interface BiConsumer<T, U> {
    void accept(T t, U u);
}

此函数式接口的 lambda 表达式不能抛出 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"); 

【讨论】:

    猜你喜欢
    • 2015-11-17
    • 2014-10-19
    • 2017-11-25
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    相关资源
    最近更新 更多