java - texto - ¿Por qué Files.lines(y Streams similares) no se cierran automáticamente?
modificar archivos txt en java (3)
El javadoc para Stream dice:
Las transmisiones tienen un método BaseStream.close () e implementan AutoCloseable, pero casi todas las instancias de transmisión en realidad no necesitan cerrarse después de su uso. En general, solo las secuencias cuya fuente sea un canal IO (como las devueltas por Files.lines (Path, Charset)) requerirán cierre. La mayoría de las transmisiones están respaldadas por colecciones, matrices o funciones de generación, que no requieren una administración de recursos especial. (Si una secuencia requiere cierre, puede declararse como recurso en una declaración try-with-resources).
Por lo tanto, la gran mayoría de las veces uno puede usar Streams en una línea, como collection.stream().forEach(System.out::println);
pero para Files.lines
y otras secuencias respaldadas por recursos, se debe usar una declaración try-with-resources o, de lo contrario, perder recursos.
Esto me parece propenso a errores e innecesario. Como Streams solo se puede repetir una vez, me parece que no hay una situación en la que el resultado de Files.lines
no se cierre tan pronto como se haya repetido, y por lo tanto la implementación debería simplemente cerrar implícitamente al final de cualquier operación de terminal ¿Estoy equivocado?
Tengo un ejemplo más específico además de la respuesta de @BrianGoetz. No olvide que el Stream
tiene métodos de escape hatch como iterator()
. Supongamos que estás haciendo esto:
Iterator<String> iterator = Files.lines(path).iterator();
Después de eso, puede llamar a hasNext()
y next()
varias veces, luego simplemente abandone este iterador: la interfaz del Iterator
perfectamente compatible con dicho uso. No hay forma de cerrar explícitamente el Iterator
, el único objeto que puede cerrar aquí es el Stream
. Así que de esta manera funcionaría perfectamente bien:
try(Stream<String> stream = Files.lines(path)) {
Iterator<String> iterator = stream.iterator();
// use iterator in any way you want and abandon it at any moment
} // file is correctly closed here.
Sí, esta fue una decisión deliberada. Consideramos ambas alternativas.
El principio de diseño operativo aquí es "la entidad que adquiere un recurso debe liberar el recurso". Los archivos no se cierran automáticamente cuando lee en EOF; esperamos que los archivos se cierren de forma explícita. Los flujos respaldados por recursos de IO son iguales.
Afortunadamente, el lenguaje proporciona un mecanismo para automatizar esto: try-with-resources. Como Stream implementa AutoCloseable, puede hacer lo siguiente:
try (Stream<String> s = Files.lines(...)) {
s.forEach(...);
}
El argumento de que "sería realmente conveniente cerrar automáticamente para poder escribirlo como un trazador de líneas" es agradable, pero sería principalmente la cola moviendo al perro. Si abrió un archivo u otro recurso, también debe estar preparado para cerrarlo. La administración de recursos efectiva y consistente triunfa sobre "Quiero escribir esto en una línea", y elegimos no distorsionar el diseño solo para preservar la línea única.
Además, si desea "escribir una línea". Puedes hacer esto:
Files.readAllLines(source).stream().forEach(...);
Puede usarlo si está seguro de que necesita un archivo completo y el archivo es pequeño. Porque no es una lectura floja.