Закройте динамическое число объектов AutoCloseable в попытке с ресурсами - PullRequest
2 голосов
/ 26 апреля 2019

Я создаю переменное количество AutoCloseable объектов в блоке try-with-resources.В любой точке выхода я хочу, чтобы все выделенные ресурсы были закрыты.

Я могу представить, что сам что-то пишу для этого, но существует ли существующая утилита, похожая на contextlib Python.ExitStack , которая будет закрывать выделенные ресурсы?Я ожидал бы, что это будет выглядеть так:

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

(Примечание: это не этот вопрос , потому что я не знаю, сколько ресурсов у меня будет заранее.

Привет, близкие избиратели Я не прошу библиотеку, я спрашиваю, как бы вы справились с задачей безопасного закрытия динамического числа AutoCloseable с, если есть языковая функцияотлично, если есть стандартная библиотечная функция, также прекрасно, если мне нужно написать свою собственную, хорошо. Если бы вы хотели , рекомендовали бы стороннюю библиотеку общего пользования, которая имеет это втогда уж точно.

Ответы [ 3 ]

1 голос
/ 27 апреля 2019

Я думаю, вы найдете класс 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

Класс по-прежнему помечен как бета-версия, но, похоже,ошиваться.Первоначальная цель заключалась в том, чтобы предоставить возможность пробного использования ресурсов без поддержки функций языка Java 7, однако полезным побочным эффектом является то, что он должен работать с динамическим числом ресурсов.

1 голос
/ 26 апреля 2019

Учитывая, что эта утилита не существует, я написал одну.Он оборачивает любые сгенерированные исключения и затем выбрасывает, только если метод 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
}
0 голосов
/ 27 апреля 2019

Возможно, вы могли бы сделать что-то вроде этого:

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