Устанавливают ли терминальные операции потоки источник?

Рассмотрим следующий код:

Path directory = Paths.get(/* some directory */);
Files.list(directory).forEach(System.out::println);

Завершает ли операция терминала (например, forEach) основной файл, который был открыт?

Обратитесь к соответствующим частям javadoc Files.list:

Возвращенный поток инкапсулирует DirectoryStream. Если требуется своевременное удаление ресурсов файловой системы, следует использовать конструкцию try-with-resources для обеспечения того, чтобы метод закрытия потока был вызван после завершения операций потока.

Если он не вызывает Stream.close(), что тогда было бы лучшей альтернативой для вызова его при создании поддерживаемого кода?

Ответ 1

Операторы терминала НЕ закрывают поток автоматически. Рассмотрим этот код:

Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed"));
list.forEach(System.out::println);

Это НЕ выводит "Закрыто".

Тем не менее, следующее: "Закрыто":

try (Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed"))) {
    list.forEach(System.out::println);
}

Таким образом, лучший способ сделать это - использовать механизм try-with-resources.

Ответ 2

Итак, быстрая проверка показывает, что forEach не закрывает DirectoryStream:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.stream.Stream;

/**
 * Created for http://stackoverflow.com/q/27381329/1266906
 */
public class FileList {
    public static void main(String[] args) {
        Path directory = Paths.get("C:\\");
        try {
            Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Close called"));
            list.forEach(System.out::println);
            // Next Line throws "java.lang.IllegalStateException: stream has already been operated upon or closed" even though "Close called" was not printed
            list.forEach(System.out::println);
        } catch (IOException | IllegalStateException e) {
            e.printStackTrace();  // TODO: implement catch
        }

        // The mentioned try-with-resources construct
        try (Stream<Path> list = Files.list(directory)) {
            list.forEach(System.out::println);
        } catch (IOException | IllegalStateException e) {
            e.printStackTrace();  // TODO: implement catch
        }

        // Own helper-method
        try {
            forEachThenClose(Files.list(directory), System.out::println);
        } catch (IOException | IllegalStateException e) {
            e.printStackTrace();  // TODO: implement catch
        }
    }

    public static <T> void forEachThenClose(Stream<T> list, Consumer<T> action) {
        try {
            list.forEach(action);
        } finally {
            list.close();
        }
    }
}

Я вижу два представленных смягчения:

  • используйте try-with-resources, как указано в Files.list JavaDoc
  • напишите свой собственный хелпер-метод, который использует finally-block

Что более удобно обслуживать, вероятно, зависит от того, сколько хелперных методов вам понадобится.