如何干净地处理stream/forEach中的“未报告的异常IOException”?

Jon*_*tan 6 java compiler-errors java-8 java-stream

我正在尝试使用 Java Streams 写入文件。据我所知,只要将异常抛出,就不必捕获异常。然而编译器会抛出一个错误(在stream.foreach()...的行上)并说

错误:未报告的异常 IOException;必须被抓住或宣布被扔出

有人可以解释这是为什么吗?(我只对使用流的解决方案感兴趣)

public void save(String file, ArrayList<String> text) throws IOException {
    FileWriter fw = new FileWriter(file);
    text.stream().forEach(x->fw.write(x +"\n"));
    fw.close();
}
Run Code Online (Sandbox Code Playgroud)

Netbeans 建议这样做,但是有没有更短的方法呢?

public void save(String file, ArrayList<String> text) throws IOException {
    FileWriter fw = new FileWriter(file);
    text.stream().forEach(x->{
        try {
            fw.write(x +"\n");
        } catch (IOException ex) {
            ...
        }
    });
    fw.close();
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 1

forEach操作需要一个Consumeraccept方法不允许抛出已检查的异常。对于您的情况,解决方案很简单:

public void save(String file, ArrayList<String> text) throws IOException {
    Files.write(Paths.get(file), text, Charset.defaultCharset());
}
Run Code Online (Sandbox Code Playgroud)

如果它确实必须是流操作,您可以使用

public void save(String file, ArrayList<String> text) throws IOException {
    Files.write(Paths.get(file),
                (Iterable<String>)()->text.stream().iterator(), Charset.defaultCharset());
}
Run Code Online (Sandbox Code Playgroud)

以这种方式编写它是有效的,因为流操作不会抛出已检查的异常,相反,外部操作Files.write可能会抛出。

处理 a 中已检查异常的通用解决方案Consumer可能是执行包装和解包的辅助方法:

interface IOConsumer<T> extends Consumer<T> {
    public default void accept(T t) {
        try { doIO(t); } catch(IOException ex) { throw new UncheckedIOException(ex); }
    }
    void doIO(T t) throws IOException;

    public static <E> void doForEach(Stream<? extends E> s, IOConsumer<? super E> c)
    throws IOException {
        try{ s.forEachOrdered(c); } catch(UncheckedIOException ex){ throw ex.getCause(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用

public void save(String file, ArrayList<String> text) throws IOException {
    try( FileWriter fw = new FileWriter(file) ) {
        IOConsumer.doForEach(text.stream(), x -> fw.write(x +"\n"));
    }
}
Run Code Online (Sandbox Code Playgroud)

此构造可确保您IOException在启动器级别正确处理电位。请注意,我close()用正确的 try-with-resource 结构替换了不安全的显式调用。

这样,您仍然可以使用任意中间流操作,即doForEach(text.stream().intermediateOp1().iOp2(), x -> fw.write(x +"\n"))除集合之外的流源。