我尝试在列表的所有项目上执行一种方法,同时忽略导致异常的条目。如果抛出异常,则忽略该项目,并且处理以下项目。最后,遇到的第一个例外是要警告出现问题。
基本上,这应该导致相同的输出,而与输入的顺序无关(如果例外是第一个或最后一个元素抛出的,则所有其他元素仍被处理(。
具体情况是:
List<Input> inputs = getInputs();
List<Output> outputs = new ArrayList<>();
MyException exc = null;
for(Input input: inputs){
try{
outputs.add(handleInput(input));
} catch(MyException e) {
if(exc != null){
exc = e;
}
}
}
if(exc != null){
throw exc;
}
可能的通用情况不会编译:
public <I, O, E extends Exception> List<O> tryAll(List<I> inputs, ExceptionFunction<I, O, E> f) throws E {
E exc = null;
List<O> outputs = new ArrayList<>();
for(I input: inputs){
try{
outputs.add(f.apply(input));
} catch(E e){ //compile error
if(exc != null){
exc = e;
}
}
}
if(exc != null){
throw exc;
}
return result;
}
其中 ExceptionFunction
是:
@FunctionalInterface
public interface ExceptionFunction<T, R, E extends Throwable> {
public R apply(T t) throws E;
}
上面的代码不起作用,因为您无法在Java中捕获通用异常。有其他方法可以实现这一目标吗?
我意识到,投掷例外可以停止对输出列表的任何访问,但是handleInput
可以突变其他标志,对此检查所有元素很重要。
处理它的一种方法是更改设计并不使用通用异常,而是使用您自己的异常(例如IgnorableException
代替E extends Throwable
(。
在您描述的情况下,您将有可能实现,您将被迫使用handleInput
方法中的IgnorableException
和捕获子句中的异常。
解决此问题的常见方法是使 tryAll
采用指示异常类型的类型令牌:
public <I, O, E extends Exception> tryAll(
List<I> inputs,
ExceptionFunction<I, O, E> function,
Class<E> exceptionType)
throws E;
然后在实现中,只需捕获更广泛的类型(例如Exception
(,然后使用throw exceptionType.cast(ex)
来确保仅重新插入给定类型的例外。像Guava的Throwables.propagateIfInstanceOf()
这样的实用程序在这里很有用。
(请注意,这是安全的,因为从Throwable
延伸的类型不能是通用的,因此异常类型将始终可以进行修复。(
如果您可以将所有异常都插入一个通用层次结构(或接受像Exception
这样的基本类型(,则可以消除E
类型参数和相应的类型标记参数。