Устанавливают ли терминальные операции потоки источник?
Рассмотрим следующий код:
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
Что более удобно обслуживать, вероятно, зависит от того, сколько хелперных методов вам понадобится.