Java 8流中的聚合运行时异常

met*_*bed 24 java exception-handling java-8 java-stream

假设我有一个抛出运行时异常的方法.我正在使用a 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) {
            ...
        }             
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我希望处理列表中的所有项目,并将各个项目的任何运行时异常收集到最终将抛出的"聚合"运行时异常中.

在我的真实代码中,我正在进行第三方API调用,这可能会引发运行时异常.我想确保处理所有项目并在最后报告任何错误.

我可以想出一些方法来解决这个问题,例如map()捕获并返回异常的函数(..shudder ..).但有没有一种本地方式来做到这一点?如果没有,还有另一种方法可以干净利落地实施吗?

Mis*_*sha 20

在这个简单的情况下,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;
    });
Run Code Online (Sandbox Code Playgroud)

但是,如果您的要求更复杂并且您更愿意坚持使用标准库,CompletableFuture则可以用来表示"成功或失败"(尽管有些瑕疵):

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);
            });
}
Run Code Online (Sandbox Code Playgroud)


Tag*_*eev 5

已经有一些Trymonad用于Java的实现.例如,我找到了更好的java8-monads库.使用它,您可以使用以下样式编写.

假设您要映射值并跟踪所有异常:

public String doStuff(String s) {
    if(s.startsWith("a")) {
        throw new IllegalArgumentException("Incorrect string: "+s);
    }
    return s.trim();
}
Run Code Online (Sandbox Code Playgroud)

我们有一些意见:

List<String> input = Arrays.asList("aaa", "b", "abc  ", "  qqq  ");
Run Code Online (Sandbox Code Playgroud)

现在我们可以将它们映射到成功的尝试并传递给您的方法,然后分别收集成功处理的数据和故障:

Map<Boolean, List<Try<String>>> result = input.stream()
        .map(Try::successful).map(t -> t.map(this::doStuff))
        .collect(Collectors.partitioningBy(Try::isSuccess));
Run Code Online (Sandbox Code Playgroud)

之后,您可以处理成功的条目:

System.out.println(result.get(true).stream()
    .map(t -> t.orElse(null)).collect(Collectors.joining(",")));
Run Code Online (Sandbox Code Playgroud)

除了所有例外,做一些事情:

result.get(false).stream().forEach(t -> t.onFailure(System.out::println));
Run Code Online (Sandbox Code Playgroud)

输出是:

b,qqq
java.lang.IllegalArgumentException: Incorrect string: aaa
java.lang.IllegalArgumentException: Incorrect string: abc  
Run Code Online (Sandbox Code Playgroud)

我个人不喜欢这个库的设计,但它可能适合你.

这里的要点有完整的例子.