Java 8流中的聚合运行时异常
假设我有一个抛出运行时异常的方法。我正在使用Java 8流中的聚合运行时异常,java,exception-handling,java-8,java-stream,Java,Exception Handling,Java 8,Java Stream,假设我有一个抛出运行时异常的方法。我正在使用流对列表中的项调用此方法 class ABC { public void doStuff(MyObject myObj) { if (...) { throw new IllegalStateException("Fire! Fear! Foes! Awake!"); } // do stuff... } public void doStuffOnLis
流
对列表中的项调用此方法
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) {
...
}
}
}
ABC类{
公共无效doStuff(MyObject MyObject){
如果(…){
抛出新的非法国家例外(“火!恐惧!敌人!觉醒!”);
}
//做些事情。。。
}
公共作废文件清单(清单MYOBJ){
试一试{
myObjs.stream().forEach(ABC:doStuff);
}捕获(聚合异常???是){
...
}
}
}
现在,我希望处理列表中的所有项,并将单个项上的任何运行时异常收集到“聚合”运行时异常中,该异常将在最后抛出
在我的真实代码中,我正在进行可能引发运行时异常的第三方API调用。我希望确保所有项目都已处理,并且在最后报告任何错误
我可以想出一些方法来解决这个问题,比如一个
map()
函数,它捕获并返回异常(…抖动…)。但是有没有一种本土的方法可以做到这一点呢?如果没有,还有其他方法可以干净地实现它吗?我能想象的唯一可能的方法是将列表中的值映射到一个monad,它将表示处理执行的结果(值成功或可丢弃失败)。然后将流折叠成单个结果,其中包含聚合的值列表,或者包含前面步骤中被抑制的值列表的一个异常
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)
public Result doStuff(列表){
return list.stream().map(this::process).reduce(RESULT\u合并)
}
公共结果处理(对象列表项){
试一试{
对象结果=/*执行处理*/listItem;
返回结果。成功(Result);
}捕获(例外e){
返回结果。失败(e);
}
}
publicstaticfinalbinarymoperator这里是映射到异常主题的一个变体
<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);
}
};
}
从现有的doStuff
方法开始。请注意,这符合功能界面消费者
现在,编写一个高阶函数,将其包装,并将其转换为一个可能返回或不返回异常的函数。我们想从flatMap
调用它,因此“可能”或“可能不”的表达方式是返回一个包含异常或空流的流。我将使用RuntimeException
作为这里的异常类型,但它当然可以是任何类型。(事实上,对选中的异常使用此技术可能很有用。)
已有一些Try
monad for Java的实现。例如,我找到了图书馆。使用它,您可以使用以下样式进行书写
假设要映射值并跟踪所有异常:
public String doStuff(String s) {
if(s.startsWith("a")) {
throw new IllegalArgumentException("Incorrect string: "+s);
}
return s.trim();
}
result.get(false).stream().forEach(t -> t.onFailure(System.out::println));
让我们输入一些信息:
List<String> input = Arrays.asList("aaa", "b", "abc ", " qqq ");
并对所有例外情况采取行动:
public String doStuff(String s) {
if(s.startsWith("a")) {
throw new IllegalArgumentException("Incorrect string: "+s);
}
return s.trim();
}
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
我个人不喜欢这个库的设计,但它可能适合你
这里有一个完整的例子。在这个简单的例子中,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
可以用来表示“成功或失败”(尽管有一些缺点):
publicstaticvoiddostuffonlist(列表myobj){
myObjs.stream()
.flatMap(o->completedFuture(o)
.然后接受(ABC::doStuff)
.handle((x,ex)->ex!=null?流(ex):null)
.join()
).减少((ex1,ex2)->{
ex1.addsupprested(ex2);
返回ex1;
}).如果存在(ex->{
抛出新的运行时异常(ex);
});
}
这感觉像是对异常机制的滥用。您在这里的实际用例是什么?正在验证一组操作并报告失败的内容?@Kayaman不太清楚。我正在进行可能引发运行时异常的第三方API调用。我想做的就是确保所有项目都已处理,并且在最后报告任何错误。您真的需要使用流吗?没有它,这是微不足道的。@Kayaman我知道,对吧?但我希望最终使用并行流
。我的流fu不太强大,不能给你一个好的答案,但基于流的解决方案至少会有点不可靠。没错。我开始使用自己的结果映射器,但希望有更好的方法。someValue
是过程的参数吗?如果是这样,您可以修复它吗?可能是您可以声明类似于SomeType result
的内容,然后将listItem
转换为result
,然后返回result.success(result)
?使用此方法而不是仅从高阶函数返回RuntimeException
,并使用map()
而不是flatMap()
,其优点是什么?@metaCube映射器函数始终必须返回某些内容。如果没有异常,它是否应该返回null?然后空值将出现在结果列表中,否则它们将被过滤掉。谢谢!我来看看这个。我喜欢你答案的早期版本:)但是嘿,总是有编辑历史。。
b,qqq
java.lang.IllegalArgumentException: Incorrect string: aaa
java.lang.IllegalArgumentException: Incorrect string: abc
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;
});
public static void doStuffOnList(List<MyObject> myObjs) {
myObjs.stream()
.flatMap(o -> completedFuture(o)
.thenAccept(ABC::doStuff)
.handle((x, ex) -> ex != null ? Stream.of(ex) : null)
.join()
).reduce((ex1, ex2) -> {
ex1.addSuppressed(ex2);
return ex1;
}).ifPresent(ex -> {
throw new RuntimeException(ex);
});
}