BiConsumer<Exception, Consumer<? super Integer>> NOTHING = (ex, unused) ->{/**/};
当我尝试修复@Holger在此answer中报告的错误时:
Stream<Integer> 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<Integer> 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<Integer> result = exceptionally(
Stream.of(new String[]{"bad"}).map(Integer::parseInt),
NOTHING
).collect(toList());
java.util.stream.Streams.StreamBuilderImpl#tryAdvance
应先重置count
,例如:
public boolean tryAdvance(Consumer<? super T> 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;
}
}
Q :它应该是jdk中的错误,因为它必须保持Stream.of
方法之间的语义一致。我是对的吗?
<T> Stream<T> exceptionally(Stream<T> source,
BiConsumer<Exception, Consumer<? super T>> exceptionally) {
class ExceptionallySpliterator extends AbstractSpliterator<T>
implements Consumer<T> {
private Spliterator<T> source;
private T value;
public ExceptionallySpliterator(Spliterator<T> source) {
super(source.estimateSize(), source.characteristics());
this.source = source;
}
@Override
public boolean tryAdvance(Consumer<? super T> action) {
Boolean state = attempt(action);
if (state == null) return true;
if (state) action.accept(value);
return state;
}
private Boolean attempt(Consumer<? super T> 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);
}
答案 0 :(得分:4)
我不会称这是一个错误,甚至不是一个意外的行为,鉴于我警告过这样的场景this comment在一个月前的链接的问题:
请记住,当抛出异常时,您不知道源迭代器是否实际提升了其内部状态,因此假设存在下一个元素可能会导致无限循环,重复失败的操作一遍又一遍。
当然,对于Stream.of(singleElement)
情况,这种情况很容易避免,并且更改两个语句action.accept(first);
和count = -1;
的顺序,会使代码更加健壮,能够从异常中恢复并不是一个有保证的功能,并且还有其他流源,这些恢复无法轻易实现。
,例如BufferedReader.lines()
返回的流。如果发生Files.lines()
,则IOException
无法强制其基础读者前进一行。
通常,这种尝试从异常中恢复,使过早假设由源引发的异常总是指示与特定的元件,而不是与整个源的问题的问题。这适用于设计的示例,您知道异常与特定元素相关联,因为您激发了它。但这不是你如何处理意外的异常,这是异常通常是什么,因为当你期望一个特定的问题,比如字符串元素不是数字格式时,你应该直接处理它们,就像在解析之前过滤掉无效字符串一样。