猿问

对于基于 I/O 的流,我应该在 flatMap 中使用 try-with-resource 吗?

AStream是一个AutoCloseable,如果基于 I/O,应该在一个try-with-resource块中使用。通过 插入的基于 I/O 的中间流flatMap()呢?例子:


try (var foos = foos()) {

   return foos.flatMap(Foo::bars).toArray(Bar[]::new);

}

对比


try (var foos = foos()) {

  return foos.flatMap(foo -> {

    try (var bars = foo.bars()) {

      return bars;

    }

  }).toArray(Bar[]::new);

}

flatMap()文档说:


每个映射流在其内容被放入该流后关闭。


嗯,这就是幸福的道路。如果中间发生异常怎么办?然后该流会保持未关闭状态并可能泄漏资源吗?那么我是否应该始终将 atry-with-resource也用于中间流?


holdtom
浏览 102回答 2
2回答

慕田峪7331174

像这样的结构没有意义return foos.flatMap(foo -> {&nbsp; &nbsp; try (var bars = foo.bars()) {&nbsp; &nbsp; &nbsp; &nbsp; return bars;&nbsp; &nbsp; }}).toArray(Bar[]::new);因为这会在流返回给调用者之前关闭流,这使得子流完全无法使用。事实上,函数的代码不可能确保关闭会发生在函数外部的适当位置。这肯定是 API 设计者决定您不必这样做的原因,而 Stream 实现将负责。这也适用于特殊情况。一旦函数将流返回给流,流仍然会确保流被关闭:try {&nbsp; &nbsp; IntStream.range(1, 3)&nbsp; &nbsp; &nbsp; &nbsp; .flatMap(i -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println("creating "+i);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return IntStream.range('a', 'a'+i)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .peek(j -> {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; System.out.println("processing sub "+i+" - "+(char)j);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(j=='b') throw new IllegalStateException();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .onClose(() -> System.out.println("closing "+i));&nbsp; &nbsp; &nbsp; &nbsp; })&nbsp; &nbsp; &nbsp; &nbsp; .forEach(i -> System.out.println("consuming "+(char)i));} catch(IllegalStateException ex) {&nbsp; &nbsp; System.out.println("caught "+ex);}creating 1processing sub 1 - aconsuming aclosing 1creating 2processing sub 2 - aconsuming aprocessing sub 2 - bclosing 2caught java.lang.IllegalStateException您可以使用条件来查看构造的 Stream 始终是关闭的。对于未处理的外部 Stream 的元素,根本不会有 Stream。对于类似.flatMap(Foo::bars)or的 Stream 操作.flatMap(foo -> foo.bars()),您可以假设一旦bars()成功创建并返回了 Stream,它将被传递给调用者并正确关闭。一个不同的场景是映射函数,它在 Stream 创建之后执行操作,这可能会失败,例如.flatMap(foo -> {&nbsp; &nbsp; Stream<Type> s = foo.bar();&nbsp; &nbsp; anotherOperation(); // Stream is not closed if this throws&nbsp; &nbsp; return s;})在这种情况下,有必要确保在例外情况下关闭,但仅在例外情况下:.flatMap(foo -> {&nbsp; &nbsp; Stream<Type> s = foo.bar();&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp; anotherOperation();&nbsp; &nbsp; } catch(Throwable t) {&nbsp; &nbsp; &nbsp; &nbsp; try(s) { throw t; } // close and do addSuppressed if follow-up error&nbsp; &nbsp; }&nbsp; &nbsp; return s;})但显然,您应该遵循一般规则来保持 lambda 简单,在这种情况下,您不需要这种保护。

ABOUTYOU

无论是否在 Stream 中,您都必须在相关位置关闭 IO 资源。该flatMap()方法是通用的流方法,因此它不知道您在其中打开的 IO 资源。但是为什么flatMap()会与任何操纵 IO 资源的方法不同呢?例如,如果您在 中操作 IO map(),如果发生异常,您可能会遇到相同的问题(不释放资源)。关闭流(如flatMap())不会使其释放在流操作中打开的所有资源。例如,一些方法可以做到这一点。File.lines(Path)但是如果你在 中打开了一些资源flatMap(),这些资源的关闭不会在流关闭时自动进行。例如这里的 flatMap 处理不会关闭FileInputStream已打开:&nbsp;...&nbsp;.stream()&nbsp;.flatMap(foo -> {&nbsp; &nbsp; try {&nbsp; &nbsp; &nbsp; &nbsp;FileInputStream fileInputStream = new FileInputStream("..."));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;//...&nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; &nbsp;catch (IOException e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// handle&nbsp; &nbsp; &nbsp;}&nbsp;})您必须明确关闭它:&nbsp;...&nbsp;.stream()&nbsp;.flatMap(foo -> {&nbsp; &nbsp; &nbsp;try (FileInputStream fileInputStream = new FileInputStream("...")){&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;//...&nbsp; &nbsp; &nbsp;} catch (IOException e) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;// handle&nbsp; &nbsp; &nbsp;}&nbsp; &nbsp; // return&nbsp;})所以是的,如果在内部使用的语句flatMap()或任何方法操作一些 IO 资源,你想在任何情况下关闭它们,用一个语句包围它try-with-resources以使它们自由。
随时随地看视频慕课网APP

相关分类

Java
我要回答