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);
            });
}