我必须定义自己的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一起使用: - (
答案 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())
我们现在可以对异常采取措施,或者只是忽略它。