为什么流api不是为exception处理而设计的?

赛程

BiConsumer<Exception, Consumer> NOTHING = (ex, unused) ->{/**/}; 

当我尝试修复@Holger在此答案中报告的错误时:

 Stream stream = Stream.of(1, 2, 3); // v--- the bug I have already fixed, it will throws RuntimeException exceptionally(stream, NOTHING).collect(ArrayList::new, (l, x) -> { l.add(x); if (x < 4) throw new RuntimeException(); }, List::addAll); 

一切正常但是当使用Stream.of(T) ,将无限地调用map(...)操作,例如:

 List result = exceptionally( // v--- infinitely call Stream.of("bad").map(Integer::parseInt), NOTHING ).collect(toList()); 

但是当我用Stream.of(T[])替换Stream.of(T)时,它再次正常工作,例如:

 // v--- return an empty list List result = exceptionally( Stream.of(new String[]{"bad"}).map(Integer::parseInt), NOTHING ).collect(toList()); 

应首先重置java.util.stream.Streams.StreamBuilderImpl#tryAdvance ,例如:

 public boolean tryAdvance(Consumer action) { Objects.requireNonNull(action); if (count == -2) { action.accept(first); count = -1;// <--- it should be call before `action.accept(first)`; return true; } else { return false; } } 

:它应该是jdk中的一个错误,因为它必须保持Stream.of方法之间的语义一致。 我对吗?

  Stream exceptionally(Stream source, BiConsumer<Exception, Consumer> exceptionally) { class ExceptionallySpliterator extends AbstractSpliterator implements Consumer { private Spliterator source; private T value; public ExceptionallySpliterator(Spliterator source) { super(source.estimateSize(), source.characteristics()); this.source = source; } @Override public boolean tryAdvance(Consumer action) { Boolean state = attempt(action); if (state == null) return true; if (state) action.accept(value); return state; } private Boolean attempt(Consumer action) { try { return source.tryAdvance(this); } catch (Exception ex) { exceptionally.accept(ex, action); return null; } } @Override public void accept(T value) { this.value = value; } } return stream( new ExceptionallySpliterator(source.spliterator()), source.isParallel() ).onClose(source::close); } 

我不会称这是一个错误 – 甚至不是一个意外的行为,因为我在一个月前的链接问题的评论中警告了这种情况:

请记住,当抛出exception时,您不知道源迭代器是否实际上提升了其内部状态,因此假设存在下一个元素会导致您进入无限循环,一遍又一遍地重复失败的操作再次。

当然,对于Stream.of(singleElement)情况,这种情况很容易避免,并且改变了两个语句的顺序, action.accept(first);count = -1; ,会使代码更加健壮,但是,能够从exception中恢复并不是一个有保证的function,并且还有其他流源,这些恢复无法轻松实现。

例如, BufferedReader.lines()返回的流。 如果发生IOException Files.lines()不能强制其底层读者前进一行。

通常,这种从exception中恢复的尝试会过早地假设源抛出的exception总是指示特定元素的问题,而不是源通常的问题。 这适用于人为的例子,你知道exception与特定元素相关联,因为你激发了它。 但这不是你如何处理意外exception,这是exception通常是什么,因为当你期望一个特定的问题,比如字符串元素不是数字格式时,你应该直接处理它们,比如过滤掉无效的字符串在解析之前。