12

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

4 回答 4

11

前体:捕获或指定要求

让我们把 lambda 写成一个匿名类:

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

我们在处理它吗?应该清楚的是,我们不是。

当我们写一个 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
}

(另见UncheckedIOException。)

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

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

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

于 2015-04-24T19:54:09.817 回答
7

如果您所关心的只是将字符串打印到文件中,请使用 aPrintStreamPrintWriter代替其他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
    }
}

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

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

interface IOConsumer<T> {
    void accept(T t) throws IOException;
}

现在我们需要编写一个将 a 转换为 a 的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);
        }
    };
}

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

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
    }
}
于 2015-04-25T06:46:57.560 回答
3

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

解决方案是改为使用 for each 循环。

于 2015-04-24T19:50:08.100 回答
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());
      } 
  } 
}
于 2015-04-25T11:02:27.560 回答