如何在Iterable.forEach中处理IOException?

Wil*_*son 10 java foreach lambda checked-exceptions java-8

我正在玩弄将一组对象写入文件的方法.为什么使用Iterable.forEach()的下面的实现不能编译?在Eclipse中,我收到的消息是没有处理IOException.这特别令人困惑,因为我似乎正在处理IOExceptions.

  public void write(Iterable<?> objects) {
    try (BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(
            new FileOutputStream("out.txt"), "UTF-8"));) {

      objects.forEach((o) -> bw.write(o.toString())); //Unhandled exception type IOException

  } catch (IOException e) {
    //handle exception
  }
}
Run Code Online (Sandbox Code Playgroud)

显然,下面的工作.我很感兴趣为什么以上不起作用以及如何解决它.

for (Object o : objects) { bw.write(o.toString()); }
Run Code Online (Sandbox Code Playgroud)

我已经检查了ConsumerIterable文档,他们似乎都没有建议如何解决这个问题.

Rad*_*def 11

前体:捕获或指定要求.

让我们将lambda写为匿名类:

objects.forEach(new Consumer<Object>() {
    public void accept(Object o) {
        bw.write(o.toString());
    }
});
Run Code Online (Sandbox Code Playgroud)

我们正在处理吗?应该很清楚,我们不是.

当我们写一个lambda表达式时,我们正在声明一个方法的主体.我们也不能throws为lambda 声明一个子句.

唯一的"解决方法"是做类似以下的事情:

try (BufferedWriter bw = new BufferedWriter(
    new OutputStreamWriter(
        new FileOutputStream("out.txt"), "UTF-8"));) {

    objects.forEach((o) -> {
        try {
            bw.write(o.toString()));
        } catch(IOException kludgy) {
            throw new UncheckedIOException(kludgy);
        }
    });

} catch (UncheckedIOException kludgy) {
    IOException cause = kludgy.getCause();
    // handle exception
}
Run Code Online (Sandbox Code Playgroud)

(另见UncheckedIOException.)

Iterable.forEach 保证包装和抛出异常,就像在该示例中一样:

操作抛出的异常会转发给调用者.

但是,最好避免forEach在抛出已检查异常的上下文中使用,或者在lambda体中捕获并处理异常.


Stu*_*rks 6

如果您只关心将字符串打印到文件,请使用PrintStreamPrintWriter替代其他Writer类.的显着特点PrintStream,并PrintWriter为他们的印刷业务不乱扔IOException.他们还会toString自动调用对象,这使得事情变得非常方便:

public void write1(Iterable<?> objects) {
    try (PrintStream ps = new PrintStream("printout.txt", "UTF-8")) {
        objects.forEach(ps::println);
    } catch (IOException ioe) {
        // handle
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你担心错误,你可以打电话PrintStream.checkError,虽然这并没有告诉你任何可能发生的错误的细节.

但是,一般的问题仍然是,如果你想在forEach不允许它的上下文(例如)中调用异常抛出方法该怎么办.这只是令人烦恼的处理,尽管只是适度.但是,它确实需要一些设置.假设我们想写一个Consumer抛出一个IOException.我们必须声明我们自己的功能接口:

interface IOConsumer<T> {
    void accept(T t) throws IOException;
}
Run Code Online (Sandbox Code Playgroud)

现在我们需要编写一个将IOConsumera 转换为a 的函数Consumer.它通过将任何IOException捕获转换UncheckedIOException为为此目的创建的异常来实现此目的.

static <T> Consumer<T> wrap(IOConsumer<? super T> ioc) {
    return t -> {
        try {
            ioc.accept(t);
        } catch (IOException ioe) {
            throw new UncheckedIOException(ioe);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

有了这些,我们现在可以重写原始示例如下:

public void write2(Iterable<?> objects) {
    try (BufferedWriter bw = new BufferedWriter(
             new OutputStreamWriter(
                 new FileOutputStream("out.txt"), "UTF-8"))) {
        objects.forEach(wrap(o -> bw.write(o.toString())));
    } catch (IOException|UncheckedIOException e) {
        //handle exception
    }
}
Run Code Online (Sandbox Code Playgroud)