Java 8流附加错误处理以供以后使用

Jee*_*87c 5 java java-8 java-stream

假设我想要重构以下方法

protected Stream<T> parseFile(File file, Consumer<File> cleanup) {
  try {
    return parser.parse(file); // returns a Stream<T>
  } catch (XmlParseException e) { // child of RuntimeException
    throw new CustomRuntimeException(e);
  } finally {
    if (file != null) {
      cleanup.accept(file);
    }
  }

  throw new IllegalStateException("Should not happen");
}
Run Code Online (Sandbox Code Playgroud)

此方法的目的是充当代理,在包装异常中的流重新抛出上附加错误处理CustomRuntimeException.因此,当我们稍后在流程中使用它时,我不必在任何地方处理这些异常,而只是处理它们CustomRuntimeException.

上游,我使用的方法如下

try {
  Stream<T> stream = parseFile(someFile);
  stream.map(t -> ...);
catch (CustomRuntimeException e) {
  // do some stuff
}
Run Code Online (Sandbox Code Playgroud)

这就是parser.parse方法的样子

public Stream<T> parse() {
  // ValueIterator<T> implements Iterator<T>, AutoCloseable
  XmlRootParser.ValueIterator<T> valueIterator = new XmlRootParser.ValueIterator(this.nodeConverter, this.reader, this.nodeLocalName, this.nodeName);
  Stream<T> stream = StreamSupport.stream(Spliterators.spliteratorUnknownSize(valueIterator, 1040), false);
  stream.onClose(valueIterator::close);
  return stream;
}
Run Code Online (Sandbox Code Playgroud)

我想要处理的异常将由ValueIterator.hasNext方法抛出.这意味着它们不会在Stream创建时被抛出,而只会在Stream使用时抛出(在流上调用foreach/map/count/collect/...).

如何在方法中parseFile很好地附加错误处理方法而不必使用流?可能吗?

显然,只有当parser.parse方法在返回之前使用其流时,此代码才有效.哪个是反对使用流.

Hol*_*ger 6

所述Stream的后端,其提供迭代逻辑,是Spliterator.

所以你可以使用这样的包装器包装元素处理Spliterator:

class Wrapper<T> implements Spliterator<T> {
    final Spliterator<T> source;
    public Wrapper(Spliterator<T> source) {
        this.source = source;
    }
    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
        try {
            return source.tryAdvance(action);
        }
        catch(XmlParseException ex) {
            throw new CustomRuntimeException(ex);
        }
    }
    @Override
    public void forEachRemaining(Consumer<? super T> action) {
        try {
            source.forEachRemaining(action);
        }
        catch(XmlParseException ex) {
            throw new CustomRuntimeException(ex);
        }
    }
    @Override public Spliterator<T> trySplit() {
        Spliterator<T> srcPrefix = source.trySplit();
        return srcPrefix == null? null: new Wrapper<>(srcPrefix);
    }
    @Override public long estimateSize() { return source.estimateSize(); }
    @Override public int characteristics() { return source.characteristics(); }
    @Override public Comparator<? super T> getComparator(){return source.getComparator();}
}
Run Code Online (Sandbox Code Playgroud)

它保留原始的所有属性,Spliterator并仅转换迭代期间抛出的异常.

那你可以像使用它一样

protected Stream<T> parseFile(File file) {
    Stream<T> s = parser.parse();
    return StreamSupport.stream(new Wrapper<>(s.spliterator()), s.isParallel())
                        .onClose(s::close);
}
Run Code Online (Sandbox Code Playgroud)

呼叫者不应忘记正确关闭流:

    ResultType result;
    try(Stream<T> s = parseFile(file)) {
        result = s.
         // other intermediate ops
         // terminal operation
    }
Run Code Online (Sandbox Code Playgroud)

要么

    ResultType result;
    try(Stream<T> s = parseFile(file)) {
        result = s.
         // other intermediate ops
         // terminal operation
    }
    finally {
        // other cleanup actions
    }
Run Code Online (Sandbox Code Playgroud)