为什么Java 1.8(函数,供应商,消费者等)中的功能接口不会抛出泛型异常?

时间:2015-02-13 08:40:37

标签: java lambda java-8

我必须定义自己的throwable功能接口,例如

@FunctionalInterface
public interface ConsumerEx<T, E extends Exception> {
    public void accept(T t) throws E;

    public default ConsumerEx<T, E> andThen(ConsumerEx<? super T, ? extends E> after) {
        return t -> {
            accept(t);
            after.accept(t);
        };
    }
}

但它无法与forEach一起使用: - (

2 个答案:

答案 0 :(得分:3)

功能接口是通用接口。但是,具有泛型异常的接口仅在特殊上下文中工作,在该上下文中,您将实例传递给将立即调用该函数并重新抛出异常的方法。

但是,它不能在不立即调用函数的任何环境中工作,例如当它在不同的线程中执行或者稍后执行时。

您的特殊消费者正是这样一种无效的方案。您不能编写委托给Consumer实例的普通ConsumerEx,并准确捕获为特定ConsumerEx实例声明的异常。由于类型擦除,您不知道E的确切类型。尝试将catched异常包装在专门的包装器RuntimeException中会更加困难。包装器异常必须是类型参数匹配E的通用异常,但您不能使用类型参数捕获泛型异常。

如果功能接口允许通用异常类型,则这些问题与JRE开发人员在parallel流执行时将面临的问题完全相同。终端操作不可能保证只重新抛出声明的已检查的通用异常。

答案 1 :(得分:1)

如果允许异常,那么这会大大增加代码的复杂性,因为任何东西都可能抛出异常。这使您的代码更难以推理,写入和读取。当被捕并采取行动时,例外也会破坏参照透明度。这会产生不良后果,并且有更好的方法。

考虑到Java选择不允许在lambda表达式中使用异常的额外复杂性。

那么函数式编程如何处理这些情况呢?我们需要一个具有预期值或包含异常的数据结构。这些通常由具有左值(错误/异常)或预期(正确)正确值的Either数据结构处理(右侧作为正确的助记符)。这被称为右偏置Either,因为预期正确的值包含正确的值。所以我们需要将抛出异常的方法转换为返回Either的函数。

这方面的一个例子是FunctionalJava的Try系列接口(https://functionaljava.ci.cloudbees.com/job/master/javadoc/)。以消费者为例,我们重用了Try0接口

public interface Try0<A, Z extends Exception> {
    A f() throws Z;
}

然后将其转换为惰性右偏(fj.data.Validation):

list.forEach(Try.f(() -> methodWithException())._1())

我们现在可以对异常采取措施,或者只是忽略它。