为什么流api不是为异常处理而设计的? [英] Why does stream api is not designed for Exception handling?

查看:86
本文介绍了为什么流api不是为异常处理而设计的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

当我尝试修复@Holger在此回答

When I try to fix the bug that is reported by @Holger in this 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(...)操作将无限调用,例如:

Everything is ok but when using Stream.of(T) the map(...) operation will be invoked infinitely, for example:

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

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

But when I replace Stream.of(T) with Stream.of(T[]), it is works fine again, for example:

//            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 应首先重置计数,例如:

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 方法之间的语义一致。我是对的吗?

Q: It should be a bug in jdk, since it must keep the semantics consistent between the Stream.of methods. Am I right?

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


推荐答案

我不会这称呼鉴于我在此评论关于一个月前的链接问题:

I wouldn’t call this a bug—not even an unexpected behavior, given the fact that I warned about such scenarios in this comment on the linked question a month ago:


请记住,当抛出异常时,你不知道源迭代器是否实际上提升了它的内部状态,所以假设有一个下一个元素可以带你进入无限循环,一遍又一遍地重复失败的操作。

Keep in mind that you don’t know whether the source iterator actually advanced its internal state or not when an exception has been thrown, so assuming that there is a next element can bring you into an infinite loop, repeating the failed operation over and over again.

当然,对于 Stream.of(singleElement)情况很容易避免,并改变两个语句的顺序, action.accept(first); count = -1; ,会使代码mor仍然,能够从异常中恢复的强大功能根本不是保证功能,并且还有其他流源,这些恢复无法轻松实现。

Of course, for the Stream.of(singleElement) case that scenario is easy to avoid and changing the order of the two statements, action.accept(first); and count = -1;, would make the code more robust, still, being able to recover from an exception is not a guaranteed feature at all and there are other stream sources for which such recovery couldn’t get implemented that easily.

例如, BufferedReader.lines()返回的流,分别为。 如果发生 IOException ,则Files.lines()无法强制其基础读者前进一行。

E.g., the streams returned by BufferedReader.lines(), resp. Files.lines() can’t force their underlying reader to advance one line if an IOException occurs.

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

Generally, this attempt to recover from an exception makes the premature assumption that an exception thrown by the source always indicates a problem with a particular element rather than a problem with the source in general. This works with the contrived example where you know that the exception is associated with a particular element because you provoked it. But this is not how you can deal with unexpected exceptions, which is what exceptions usually are, as when you expect a particular problem, like string elements not being in number format, you should handle them straight-forwardly, like filtering out the invalid strings before parsing.

这篇关于为什么流api不是为异常处理而设计的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆