4

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

我可以想象自己写一些东西来做到这一点,但是是否有一个类似于Python 的 contextlib.ExitStack的现有实用程序可以关闭分配的资源?我希望它看起来像这样:

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

(注意:这不是这个问题,因为我不知道我会提前有多少资源。

嘿密切的选民我不是要一个库,我是在问你如何完成安全关闭动态数量AutoCloseables 的任务,如果有它的语言功能,很好,如果有标准库函数,也很好,如果我必须自己写,很好。如果您想推荐一个常用的第三方库,其中包含它,那么当然可以。

4

3 回答 3

2

我想你会发现 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 体验,但是一个有用的副作用是它应该使用动态数量的资源。

于 2019-04-26T21:08:32.107 回答
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
}
于 2019-04-26T17:15:17.153 回答
0

也许你可以做这样的事情:

<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);
于 2019-04-26T21:23:47.633 回答