如何在Iterable.forEach中处理IOException?

时间:2015-04-24 19:41:49

标签: java foreach lambda java-8 checked-exceptions

我正在玩弄将一组对象写入文件的方法。为什么使用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
  }
}

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

for (Object o : objects) { bw.write(o.toString()); }

我已查看ConsumerIterable文档,但他们似乎都没有建议如何解决这个问题。

4 个答案:

答案 0 :(得分:10)

前体:The Catch or Specify Requirement

让我们将lambda写为匿名类:

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

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

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

围绕它的唯一方式&#34;将做类似以下的事情:

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
}

(另见UncheckedIOException。)

Iterable.forEach保证在该示例中包装和抛出异常有效:

  

操作引发的异常被转发给调用者。

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

答案 1 :(得分:6)

如果您只关心将字符串打印到文件,请使用PrintStreamPrintWriter代替其他Writer类。 PrintStreamPrintWriter的显着特征是它们的打印操作不会抛出IOException。他们还会自动在对象上调用toString,这样可以非常方便:

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

如果您担心错误,可以致电PrintStream.checkError,但这并不能告诉您有关可能发生的任何错误的任何细节。

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

IOException

现在我们需要编写一个将interface IOConsumer<T> { void accept(T t) throws IOException; } 转换为IOConsumer的函数。它通过将其捕获的任何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);
        }
    };
}

答案 2 :(得分:2)

问题是接口accept中的方法Consumer未声明为抛出异常。因此,您不能使用在lambda中抛出已检查异常的方法。

解决方案是使用a代替每个循环。

答案 3 :(得分:1)

一般情况下,如果发生任何异常,我们不会将数据写入文件。牢记这一点,我们可以写下own consumer which will wrap the checked exception into an unchecked exception。这样你就可以摆脱编译时错误。 请尝试以下代码

import java.io.*;
import java.util.*;
public class HelloWorld{

     public static void main(String []args){
        write(Arrays.asList(1,2,3));
     }

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

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

  } catch (Exception e) {
    //handle exception
  } 
  }
  public static void myConsumerThrowsRuntimeException(Writer writer , String data){
      try{
          writer.write(data);
      }catch(IOException e){

          throw new RuntimeException("Cannot write Data error is "+e.getMessage());
      } 
  } 
}