【问题标题】:Unhandled IOException Inside Java List.stream.forEach Lambda ExpressionJava List.stream.forEach Lambda 表达式中未处理的 IOException
【发布时间】:2015-04-24 04:34:37
【问题描述】:

我正在使用 Java 8 中引入的 Stream API 为列表中的每个字符串运行一个方法。

public boolean initFile() throws IOException
{
    if (this.outFile.exists()) {
        this.outFile.delete();
    }
    return this.outFile.createNewFile();
}

public void writeStringToFile(String str, boolean addNewLine) throws IOException
{
    if (this.mode != FileMode.READ) {
        if (this.initFile()) {
            FileWriter fileWriter;
            if (this.mode == FileMode.APPEND)
                fileWriter = new FileWriter(outFile.getAbsolutePath(), true);
            else
                fileWriter = new FileWriter(outFile.getAbsolutePath());
            fileWriter.write(str);
            if (addNewLine)
                fileWriter.write("\n");
            fileWriter.close();
        } else {
            throw new IOException("IO Error: The file could not be initialized.");
        }
    } else {
        throw new IOException("Cannot write to a file with the mode FileMode.READ");
    }
}

public void writeListToFile(List<String> strings, boolean addNewLine) throws IOException
{
    strings.stream().forEach(s -> this.writeStringToFile(s, addNewLine)); // Unhandled IOException from writeStringToFile
}

如您所见,方法签名声明抛出所有 IOException,流中的 writeToString 方法有可能抛出 IOException。但是,Java 编译器给了我一个错误,指出流线上有一个未处理的 IOException。

为什么流中的异常不会像方法中的任何其他异常一样被抛出?有什么方法可以在不在 forEach lambda 表达式中添加 try-catch 语句的情况下解决这个问题?

【问题讨论】:

  • 如果抛出 IOException,你希望发生什么?请注意,这与 lambdas 和常规异常规则无关。 Stream#forEachConsumer#accept 不声明抛出此异常。
  • 你能用你的 initFile 方法声明更新你的问题吗
  • 我已经添加了 initFile 方法。请注意,所有 IOExceptions 都是有意抛出的。
  • 您的代码不合逻辑。如果文件存在,则将其删除,因此支持附加模式毫无意义,因为您总是在initFile() 之后附加到新文件。另一方面,由于您实际上总是创建一个新文件,因此您根本不需要initFile() 方法,new FileWriter(…) 会做到这一点。换句话说,当您的模式不是APPEND 时,您可能会删除一个文件、创建一个新文件、再次隐式删除它并创建一个新文件……

标签: java file exception lambda java-stream


【解决方案1】:

你必须像这样在 writeListToFile 方法中使用 try catch:

  public void writeListToFile(List<String> strings, boolean addNewLine) throws Exception {
    strings.stream().forEach(s -> {
        try {
            writeStringToFile(s, addNewLine);
        } catch (IOException ex) {
            //you can Log it immediately or throw ex;
            Logger.getLogger(C.class.getName()).log(Level.SEVERE, null, ex);
        }
    });
    // Unhandled IOException from writeStringToFile
}

为什么? 看这点s -> {}。这里{}表示编译器make new Interface(Consumer)

 void forEach(Consumer<? super T> action);

new Class 实现了这个接口,并将s 参数传递给它的accept 方法。你能在你的方法中抛出new Class 的异常吗?不,所以你必须尝试捕获 foreach 的每一项

【讨论】:

  • 我对 Stream API 的整体理解非常幼稚。您似乎对底层流程了如指掌,在我看来,这些流程有点像黑匣子。你有关于消费者/供应商/谓词/等的好的外行资源吗?
  • google new in java 8.read subject.research你坚持的内容并写博客以完全理解。当你写博客时,你必须教别人并且必须更清楚地了解主题
【解决方案2】:

不要重新发明轮子。使用 Java API 提供的功能。

public void writeListToFile(List<String> strings, boolean addNewLine) throws IOException
{
  if(this.mode == FileMode.READ)
      throw new IOException("Cannot write to a file with the mode FileMode.READ");

  Files.write(outFile.toPath(), addNewLine? strings:
      Collections.singleton(strings.stream().collect(Collectors.joining())),
    this.mode==FileMode.APPEND? StandardOpenOption.APPEND: StandardOpenOption.CREATE);
}

Files.write 将写入Iterable 的所有行(包括Collections),仅此而已。您支持addNewLine 选项的障碍可以通过将所有Strings 合并为一个来解决,addNewLinefalse,因此它们之间不会有换行符。否则,Files.write 会将换行符附加到每个字符串。

手动处理文件是否存在的测试或尝试删除或创建它们是没有意义的。无论您是创建自己的Writer 还是使用Files.write,这些实现都会报告存在/不存在的失败或通过IOExceptions 创建目标文件的尝试失败,因此您不需要这样做自己动手。

更糟糕的是,无论File.exists()File.delete()File.createNewFile() 可能返回,在您后续的 I/O 操作中已经是过时的信息,例如创建/使用Writer,不保证后续操作成功与否。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    • 2013-06-30
    相关资源
    最近更新 更多