[Somjit's][1] 答案显示了正确的方法,但是它使用外部变量 (errors) 来累积错误。一般来说,这是不鼓励的,因为我们应该避免全局/外部状态。
您可以使用 vavr 的 [partition][2] 方法将流分成两部分:一是有错误的,一是经过验证的整数。它们都被放入一个元组中:
public void composeExceptions() {
final Tuple2<Stream<Either<IllegalArgumentException, Integer>>, Stream<Either<IllegalArgumentException, Integer>>> both = Stream.range(1, 11)
.map(this::validate)
.partition(Either::isLeft);
both._1.map(Either::getLeft).forEach(e -> System.out.println("Got error: " + e.getMessage()));
both._2.map(Either::get).forEach(i -> System.out.println("Validated correctly: " + i));
}
编辑
实际上还有其他选择,例如:
Stream
.range(1, 11)
.map(this::validate)
.toJavaStream()
.collect(Collectors.teeing(
Collectors.filtering(Either::isLeft, toList()),
Collectors.filtering(Either::isRight, toList()),
(errors, ints) -> new Tuple2<>(errors.stream().map(Either::getLeft), ints.stream().map(Either::get))));
它使用teeing,这是一个来自java API 的非常有趣的收集器。不幸的是,它混合了 vavr 和 java API,这不是很好,也不是很糟糕。
还有:
Stream
.range(1, 11)
.map(this::validate)
.collect(
() -> new Tuple2<>(List.<RuntimeException>empty().asJavaMutable(), List.<Integer>empty().asJavaMutable()),
(tuple, either) -> {
either.peekLeft(tuple._1::add);
either.peek(tuple._2::add);
},
(t1, t2) -> {
t1._1.addAll(t2._1);
t1._2.addAll(t2._2);
}
)
.map((exceptions, integers) -> new Tuple2<>(List.ofAll(exceptions), List.ofAll(integers)));```
which uses vavr API only but underneath uses java `List` since a mutable structure is required here.
[1]: https://stackoverflow.com/a/67556075/542270
[2]: https://www.javadoc.io/doc/io.vavr/vavr/latest/io/vavr/collection/Traversable.html#partition(java.util.function.Predicate)