Java 8 流式传输并尝试使用资源

2022-09-01 10:25:48

我认为流API是为了让代码更容易阅读。我发现了一些很烦人的东西。接口扩展接口。Streamjava.lang.AutoCloseable

因此,如果要正确关闭流,则必须使用 try with 资源。

清单 1.不是很好,溪流没有关闭。

public void noTryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    @SuppressWarnings("resource") List<ImageView> collect = photos.stream()
        .map(photo -> new ImageView(new Image(String.valueOf(photo))))
        .collect(Collectors.<ImageView>toList());
}

清单 2.有 2 个嵌套尝试

public void tryWithResource() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream()) {
        try (Stream<ImageView> map = stream
                .map(photo -> new ImageView(new Image(String.valueOf(photo)))))
        {
            List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
        }
    }
}

清单 3.当返回流时,必须同时关闭 函数 和 函数。mapstream()map()

public void tryWithResource2() {
    Set<Integer> photos = new HashSet<Integer>(Arrays.asList(1, 2, 3));

    try (Stream<Integer> stream = photos.stream(); Stream<ImageView> map = stream.map(photo -> new ImageView(new Image(String.valueOf(photo)))))
    {
        List<ImageView> collect = map.collect(Collectors.<ImageView>toList());
    }
}

我举的例子没有任何意义。为了这个例子,我用 替换了 jpg 图像。但不要让你被这些细节分散注意力。PathInteger

使用那些自动关闭的流的最佳方式是什么。我不得不说,我对我展示的3个选项中的任何一个都不满意。你觉得怎么样?还有其他更优雅的解决方案吗?


答案 1

您正在使用它可能抑制有关未关闭资源的警告。这不是 发出的警告之一。Web 搜索似乎表明,如果 未关闭 ,Eclipse 会发出警告。@SuppressWarnings("resource")javacAutoCloseable

根据Java 7规范引入,这是一个合理的警告:AutoCloseable

不再需要时必须关闭的资源。

但是,放宽了 Java 8 规范,删除了“必须关闭”子句。它现在说,部分,AutoCloseable

可能保存资源的对象...直到它被关闭。

基类实现 AutoCloseable 是可能的,事实上也是很常见的,即使并非所有子类或实例都包含可释放的资源。对于必须完全通用地运行的代码,或者当已知 AutoCloseable 实例需要资源释放时,建议使用 try-with-resources 构造。但是,当使用同时支持基于 I/O 和非基于 I/O 的表单(如 Stream)时,在使用非基于 I/O 的表单时,通常不需要尝试使用资源块。

Lambda专家组对这一问题进行了广泛讨论。此消息总结了决策。除其他事项外,它还提到了对规范(上面引用)和规范(由其他答案引用)的更改。它还提到可能需要针对更改的语义调整 Eclipse 代码检查器,大概不会无条件地为对象发出警告。显然,这条信息没有传达给Eclipse的人,或者他们还没有改变它。AutoCloseableBaseStreamAutoCloseable

总而言之,如果 Eclipse 警告导致您认为需要关闭所有对象,那是不正确的。只有某些特定对象需要关闭。Eclipse 需要修复(如果尚未修复)才能不为所有对象发出警告。AutoCloseableAutoCloseableAutoCloseable


答案 2

仅当流需要对自身进行任何清理(通常是 I/O)时,才需要关闭流。您的示例使用 HashSet,因此不需要关闭它。

javadoc:

通常,只有源是 IO 通道的流(例如 Files.lines(Path,Charset)返回的流)才需要关闭。大多数流由集合、数组或生成函数提供支持,这些函数不需要特殊的资源管理。

因此,在您的示例中,这应该没有问题

List<ImageView> collect = photos.stream()
                       .map(photo -> ...)
                       .collect(toList());

编辑

即使需要清理资源,也应该只能使用一次资源试用。让我们假设您正在读取一个文件,其中文件中的每一行都是图像的路径:

 try(Stream<String> lines = Files.lines(file)){
       List<ImageView> collect = lines
                                  .map(line -> new ImageView( ImageIO.read(new File(line)))
                                  .collect(toList());
  }

推荐