简体   繁体   English

在 try-with-resources 中关闭动态数量的 AutoCloseable 对象

[英]Close a dynamic number of AutoCloseable objects in try-with-resources

I am creating a variable amount of AutoCloseable objects in a try-with-resources block.我正在try-with-resources块中创建可变数量的AutoCloseable对象。 At any point of exit, I want all of the allocated resources closed.在任何退出点,我都希望关闭所有分配的资源。

I can imagine writing something myself to do this, but is there an existing utility similar to Python's contextlib.ExitStack that will close allocated resources?我可以想象自己编写一些东西来做到这一点,但是是否有类似于Python 的 contextlib.ExitStack的现有实用程序可以关闭分配的资源? I would expect it to look like this:我希望它看起来像这样:

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

(Note: this is not this question because I don't know how many resources I'll have ahead of time. (注意:这不是这个问题,因为我不知道我会提前拥有多少资源。

Hey close voters I'm not asking for a library, I'm asking how you would accomplish the task of safely closing a dynamic number of AutoCloseable s, if there's a language feature for it, great, if there's a standard library function, also great, if I have to write my own, fine.嘿亲密的选民,我不是要图书馆,我是问你如何完成安全关闭动态数量的AutoCloseable的任务,如果有语言功能,很好,如果有标准库功能,还有太好了,如果我必须自己写,那很好。 If you'd like to recommend a third-party library in common use that has this in it then sure.如果您推荐的是,在它的话,当然有这个共同使用一个第三方库。

Given that this utility does not appear to exist, I wrote one.鉴于此实用程序似乎不存在,我写了一个。 It wraps up any thrown exceptions and then only throws if a resource's close() threw.它包含任何抛出的异常,然后仅在资源的 close() 抛出时抛出。 Always closes everything before returning.在返回之前总是关闭所有东西。

public class ClosingException extends Exception { }

And

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<>();
}

And use:并使用:

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

I think you'll find Guava's Closer class to be what you need here:我想你会发现 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

The class is still marked as Beta mind you, but has appeared to stick around.请注意,该课程仍被标记为 Beta,但似乎一直存在。 The original intent was to provide a try-with-resources experience without Java 7 language feature support, however a useful side effect is that it should work with a dynamic number of resources.最初的目的是在没有 Java 7 语言特性支持的情况下提供一种尝试资源的体验,但是一个有用的副作用是它应该使用动态数量的资源。

Perhaps you could do something like this:也许你可以这样做:

<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);
}

Example invocation:示例调用:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM