【问题标题】:How to cleanly deal with "unreported exception IOException" in stream/forEach?如何干净地处理流/forEach 中的“未报告的异常 IOException”?
【发布时间】:2016-08-22 23:41:43
【问题描述】:

我正在尝试使用 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();
}

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

【问题讨论】:

标签: java compiler-errors java-8 java-stream


【解决方案1】:

不幸的是,没有简单的解决方案。需要捕获引发检查异常的 Lambda 表达式。可能最简洁的方法是在单独的方法中将已检查的异常转换为未检查的异常。

try {
    text.forEach(this::write);
catch (UncheckedIOException ex) {
    ...
}

private void write(String line) throws UncheckedIOException {
    try {
        fw.write(line +"\n");
    } catch (IOException ex) {
        throw new UncheckedIOException(ex);
    }
});

不过,老实说,除了让您的 save 方法更简洁之外,这并没有比 NetBean 的建议更简洁。

如果您不清楚选中/未选中异常的区别,请阅读接受的答案here

注意List 有一个forEach 方法(实际上IterableList 扩展的)所以stream() 是多余的。

【讨论】:

  • UncheckedIOException 应该包含原始原因:throw new UncheckedIOException(ex);。或者,您可以只使用throw ex; 将异常简单地传播到调用堆栈。在这种情况下,后面的选择似乎是合理的,因为 save() 已经用 throws IOException 声明。
  • @Code-Apprentice 谢谢 - 很好。你是说throw exwrite 里面吗?那是行不通的,因为它会抛出一个 unchecked 并且 ex 被选中。
  • 在最初发布该评论后,我对其进行了多次编辑。不确定您是否看到了最新版本。我认为throw ex; 在这种情况下更可取,因为save() 已经有一个令人满意的throws 子句。
  • 按照我阅读 OP 的方式,最初的问题源于 lambda 表达式,而不是一般的 throws 子句。
【解决方案2】:

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

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

如果它真的必须是流操作,你可以使用

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

这样写是因为流操作不会抛出检查异常,而是外部操作Files.write 可能。

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

你可以像这样使用

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

此构造可确保您在发起者级别正确处理潜在的IOExceptions。请注意,我用正确的 try-with-resource 构造替换了您不安全的显式 close() 调用。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 2021-11-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-06
    • 2021-05-31
    相关资源
    最近更新 更多