为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

holdtom 2022-06-30 18:37:23
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也用于中间流?
查看完整描述

2 回答

?
慕田峪7331174

TA贡献1828条经验 获得超13个赞

像这样的结构没有意义


return foos.flatMap(foo -> {

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

        return bars;

    }

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

因为这会在流返回给调用者之前关闭流,这使得子流完全无法使用。


事实上,函数的代码不可能确保关闭会发生在函数外部的适当位置。这肯定是 API 设计者决定您不必这样做的原因,而 Stream 实现将负责。


这也适用于特殊情况。一旦函数将流返回给流,流仍然会确保流被关闭:


try {

    IntStream.range(1, 3)

        .flatMap(i -> {

            System.out.println("creating "+i);

            return IntStream.range('a', 'a'+i)

                    .peek(j -> {

                        System.out.println("processing sub "+i+" - "+(char)j);

                        if(j=='b') throw new IllegalStateException();

                    })

                    .onClose(() -> System.out.println("closing "+i));

        })

        .forEach(i -> System.out.println("consuming "+(char)i));

} catch(IllegalStateException ex) {

    System.out.println("caught "+ex);

}

creating 1

processing sub 1 - a

consuming a

closing 1

creating 2

processing sub 2 - a

consuming a

processing sub 2 - b

closing 2

caught java.lang.IllegalStateException

您可以使用条件来查看构造的 Stream 始终是关闭的。对于未处理的外部 Stream 的元素,根本不会有 Stream。


对于类似.flatMap(Foo::bars)or的 Stream 操作.flatMap(foo -> foo.bars()),您可以假设一旦bars()成功创建并返回了 Stream,它将被传递给调用者并正确关闭。


一个不同的场景是映射函数,它在 Stream 创建之后执行操作,这可能会失败,例如


.flatMap(foo -> {

    Stream<Type> s = foo.bar();

    anotherOperation(); // Stream is not closed if this throws

    return s;

})

在这种情况下,有必要确保在例外情况下关闭,但仅在例外情况下:


.flatMap(foo -> {

    Stream<Type> s = foo.bar();

    try {

        anotherOperation();

    } catch(Throwable t) {

        try(s) { throw t; } // close and do addSuppressed if follow-up error

    }

    return s;

})

但显然,您应该遵循一般规则来保持 lambda 简单,在这种情况下,您不需要这种保护。


查看完整回答
反对 回复 2022-06-30
?
ABOUTYOU

TA贡献1812条经验 获得超5个赞

无论是否在 Stream 中,您都必须在相关位置关闭 IO 资源。

该flatMap()方法是通用的流方法,因此它不知道您在其中打开的 IO 资源。

但是为什么flatMap()会与任何操纵 IO 资源的方法不同呢?例如,如果您在 中操作 IO map(),如果发生异常,您可能会遇到相同的问题(不释放资源)。

关闭流(如flatMap())不会使其释放在流操作中打开的所有资源。例如

,一些方法可以做到这一点。File.lines(Path)但是如果你在 中打开了一些资源flatMap(),这些资源的关闭不会在流关闭时自动进行。

例如这里的 flatMap 处理不会关闭FileInputStream已打开:


 ...

 .stream()

 .flatMap(foo -> {

    try {

       FileInputStream fileInputStream = new FileInputStream("..."));                                  

       //...

     }

     catch (IOException e) {

         // handle

     }


 })

您必须明确关闭它:


 ...

 .stream()

 .flatMap(foo -> {

     try (FileInputStream fileInputStream = new FileInputStream("...")){

         //...

     } catch (IOException e) {

         // handle

     }

    // return

 })

所以是的,如果在内部使用的语句flatMap()或任何方法操作一些 IO 资源,你想在任何情况下关闭它们,用一个语句包围它try-with-resources以使它们自由。


查看完整回答
反对 回复 2022-06-30
  • 2 回答
  • 0 关注
  • 101 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信