【问题标题】:Close a dynamic number of AutoCloseable objects in try-with-resources在 try-with-resources 中关闭动态数量的 AutoCloseable 对象
【发布时间】:2019-09-16 05:09:43
【问题描述】:

我正在try-with-resources 块中创建数量不定的AutoCloseable 对象。在任何退出点,我都希望关闭所有分配的资源。

我可以想象自己编写一些东西来执行此操作,但是是否存在类似于Python's contextlib.ExitStack 的现有实用程序可以关闭分配的资源?我希望它看起来像这样:

try (ExitStack exitStack = new ExitStack()) {
    List<Widget> widgets = new ArrayList<>();
    for (...) {
        widgets.add(exitStack.add(new Widget()));
    }
    // use widgets
}

(注意:这不是this question,因为我不知道我会提前多少资源。

Hey close voters 我不是要图书馆,我要问的是,如果有语言功能,你将如何安全地关闭动态数量的AutoCloseables 的任务很好,如果有标准库函数,也很好,如果我必须自己编写,很好。如果你喜欢推荐一个常用的第三方库,里面有这个,那么当然。

【问题讨论】:

  • 你怎么会不知道将在多少资源中编程?
  • @Frontear 文件名、IP 地址等列表,作为输入。
  • 为什么不在for循环中使用try-with-resources
  • @lealceldeiro 假设我需要它们同时打开。我可以更新以使其更清晰。
  • 我不知道任何提供该功能的内置类。但是实现ExitStack 之类的东西应该不会那么难,因为它可能只包含AutoCloseables 的集合,然后在自己的close() 方法中关闭它们(使用简单的循环并捕获每个元素的异常)。跨度>

标签: java java-8 try-with-resources autocloseable


【解决方案1】:

我想你会发现 Guava 的 Closer 类就是你需要的:

try (Closer closer = Closer.create()) {
   InputStream in1 = closer.register(new FileInputStream("foo"));
   InputStream in2 = closer.register(new FileInputStream("bar"));
   // use in1 and in2
}
// in2 and in1 closed in that order

请注意,该课程仍被标记为 Beta,但似乎仍然存在。最初的意图是在不支持 Java 7 语言功能的情况下提供 try-with-resources 体验,但一个有用的副作用是它应该使用动态数量的资源。

【讨论】:

  • 几乎 :/ register 接受 Closeable 而不是 AutoCloseable
【解决方案2】:

鉴于此实用程序似乎不存在,我写了一个。它封装了所有抛出的异常,然后仅在资源的 close() 抛出时才抛出。总是在返回之前关闭所有内容。

public class ClosingException extends Exception { }

import java.util.Deque;
import java.util.ArrayDeque;

public final class ClosingStack implements AutoCloseable {
  public void close() throws ClosingException {
    ClosingException allClosingExceptions = new ClosingException();
    while (!resources.isEmpty()) {
      try {
        resources.removeLast().close();
      } catch (Throwable e) {
        allClosingExceptions.addSuppressed(e);
      }
    }
    if (allClosingExceptions.getSuppressed().length != 0) {
      throw allClosingExceptions;
    }
  }

  public <T extends AutoCloseable> T add(T resource) {
    resources.addLast(resource);
    return resource;
  }


  private Deque<AutoCloseable> resources = new ArrayDeque<>();
}

并使用:

try (ClosingStack closingStack = new ClosingStack()) {
    List<Widget> widgets = new ArrayList<>();
    for (...) {
        widgets.add(closingStack.add(new Widget()));
    }
    // use widgets
}

【讨论】:

  • 我觉得不错。不过我会改变一件事:而不是ArrayDeque,我可能会使用LinkedListDeque 接口。您不需要随机访问可关闭对象,因此链接列表将在此处节省一些资源。 :)
  • @Thomas:您认为节省了哪些资源?在实践中,ArrayDeques 在几乎所有方面都更便宜。
  • @LouisWasserman 阅读其他地方证实。我原本预计ArrayDeque 会更快
  • @LouisWasserman:既然你在这里,我想知道你是否会评论 Guava 的 Closer 实用程序是否适用于这里。我知道其目的是在没有语言支持的情况下支持 try-with-resources 行为,但即使使用 try-with-resources 似乎也大致相当于这里给出的内容?
  • 我会为此创建一个特殊的 Exception 子类型来抛出。
【解决方案3】:

也许你可以这样做:

<T extends AutoCloseable> void recursively(
    List<T> things,
    Iterator<? extends Supplier<? extends T>> thingSuppliers,
    Consumer<List<T>> whenEmpty) {
  if (!thingSuppliers.hasNext()) {
    // No more to create. Pass all the things to the consumer.
    whenEmpty.accept(things);
    return;
  }

  // Create a new thing, and make a recursive call. This thing gets
  // closed as the stack unwinds.
  try (T thing = thingSuppliers.next().get()) {
    things.add(thing);
    recursively(things, thingSuppliers, whenEmpty);
  }
}

// Some means of starting the recursion.
<T extends AutoCloseable> void recursively(
    Iterable<? extends Supplier<? extends T>> thingSuppliers,
    Consumer<List<T>> whenEmpty) {
  recursively(new ArrayList<>(), thingSuppliers.iterator(), whenEmpty);
}

调用示例:

recursively(
    Arrays.asList(Widget::new, Widget::new), 
    System.out::println);

【讨论】:

    猜你喜欢
    • 2015-08-13
    • 2019-03-12
    • 2017-12-04
    • 2014-05-05
    • 2020-01-01
    • 1970-01-01
    • 2016-09-29
    • 1970-01-01
    • 2016-04-20
    相关资源
    最近更新 更多