假设我有一个抛出运行时异常的方法。我正在使用Stream
在列表中的项目上调用此方法。
class ABC {
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
public void doStuffOnList(List<MyObject> myObjs) {
try {
myObjs.stream().forEach(ABC:doStuff);
} catch(AggregateRuntimeException??? are) {
...
}
}
}
现在我希望处理列表中的所有项目,并将各个项目的任何运行时异常收集到最终将被抛出的“聚合”运行时异常中。
在我的实际代码中,我正在进行第三方API调用,这可能会引发运行时异常。我想确保处理所有项目并在最后报告任何错误。
我可以想出一些方法来解决这个问题,例如捕获并返回异常的map()
函数( .. shudder .. )。但有没有一种本地方式来做到这一点?如果没有,还有另一种方法可以干净利落地实施吗?
答案 0 :(得分:17)
在for(int i = 1; i <=20; i++)
{
excelRange = (Excel.Range)excelWorkSheet.Cells[i, 1];
if (!string.IsNullOrEmpty(excelRange.Text.ToString()))
{
((Range)excelWorkSheet.Rows[i]).Delete(excelRange);
}
}
方法为doStuff
并且您只关心异常的简单情况下,您可以保持简单:
void
但是,如果您的要求更复杂,并且您更愿意坚持使用标准库,myObjs.stream()
.flatMap(o -> {
try {
ABC.doStuff(o);
return null;
} catch (RuntimeException ex) {
return Stream.of(ex);
}
})
// now a stream of thrown exceptions.
// can collect them to list or reduce into one exception
.reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw ex;
});
可以用来代表“成功或失败”(虽然有些瑕疵):
CompletableFuture
答案 1 :(得分:5)
已经有一些Try
monad用于Java的实现。例如,我找到了better-java8-monads库。使用它,您可以使用以下样式编写。
假设您要映射您的值并跟踪所有异常:
public String doStuff(String s) {
if(s.startsWith("a")) {
throw new IllegalArgumentException("Incorrect string: "+s);
}
return s.trim();
}
让我们有一些意见:
List<String> input = Arrays.asList("aaa", "b", "abc ", " qqq ");
现在我们可以将它们映射到成功的尝试并传递给您的方法,然后分别收集成功处理的数据和故障:
Map<Boolean, List<Try<String>>> result = input.stream()
.map(Try::successful).map(t -> t.map(this::doStuff))
.collect(Collectors.partitioningBy(Try::isSuccess));
之后,您可以处理成功的条目:
System.out.println(result.get(true).stream()
.map(t -> t.orElse(null)).collect(Collectors.joining(",")));
在所有例外情况下做一些事情:
result.get(false).stream().forEach(t -> t.onFailure(System.out::println));
输出结果为:
b,qqq
java.lang.IllegalArgumentException: Incorrect string: aaa
java.lang.IllegalArgumentException: Incorrect string: abc
我个人不喜欢这个库的设计方式,但它可能适合你。
这里有gist完整示例。
答案 2 :(得分:2)
以下是映射到例外主题的变体。
从现有的doStuff
方法开始。请注意,这符合功能接口Consumer<MyObject>
。
public void doStuff(MyObject myObj) {
if (...) {
throw new IllegalStateException("Fire! Fear! Foes! Awake!");
}
// do stuff...
}
现在编写一个包含此函数的高阶函数,并将其转换为可能会或可能不会返回异常的函数。我们想从flatMap
调用它,因此表达“可能会或可能不会”的方式是返回包含异常或空流的流。我会在这里使用RuntimeException
作为例外类型,但当然它可以是任何东西。 (事实上,将此技术用于检查异常可能很有用。)
<T> Function<T,Stream<RuntimeException>> ex(Consumer<T> cons) {
return t -> {
try {
cons.accept(t);
return Stream.empty();
} catch (RuntimeException re) {
return Stream.of(re);
}
};
}
现在重写doStuffOnList
以在流中使用它:
void doStuffOnList(List<MyObject> myObjs) {
List<RuntimeException> exs =
myObjs.stream()
.flatMap(ex(this::doStuff))
.collect(Collectors.toList());
System.out.println("Exceptions: " + exs);
}
答案 3 :(得分:0)
我能想象的唯一可能的方法是将列表中的值映射到monad,这将代表处理执行的结果(带有值的成功或带有throwable的失败)。然后将您的流折叠为具有聚合值列表的单个结果,或者使用前面步骤中的已抑制列表列表中的一个例外。
public Result<?> doStuff(List<?> list) {
return list.stream().map(this::process).reduce(RESULT_MERGER)
}
public Result<SomeType> process(Object listItem) {
try {
Object result = /* Do the processing */ listItem;
return Result.success(result);
} catch (Exception e) {
return Result.failure(e);
}
}
public static final BinaryOperator<Result<?>> RESULT_MERGER = (left, right) -> left.merge(right)
结果实施可能会有所不同,但我认为你明白了。