On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev <tval...@openjdk.org> wrote:
> Currently, when the stream holds a resource, it's necessary to wrap it with > try-with-resources. This undermines the compact and fluent style of stream > API calls. For example, if we want to get the `List` of files inside the > directory and timely close the underlying filehandle, we should use something > like this: > > > List<Path> paths; > try (Stream<Path> stream = Files.list(Path.of("/etc"))) { > paths = stream.toList(); > } > // use paths > > > I suggest to add a new default method to Stream interface named > `consumeAndClose`, which allows performing terminal stream operation and > closing the stream at the same time. It may look like this: > > > default <R> R consumeAndClose(Function<? super Stream<T>, ? extends R> > function) { > Objects.requireNonNull(function); > try(this) { > return function.apply(this); > } > } > > > Now, it will be possible to get the list of the files in the fluent manner: > > > List<Path> list = Files.list(Path.of("/etc")).consumeAndClose(Stream::toList); Gosh, mailing list -> Github bridge didn't handle formatting well, so I'm posting this again via Github. Sorry for duplication... This RFR is closed, but inspired by John's discussion and fueled by grief that I have each time when I try to combine Stream processing with resources that throw checked exceptions on construction and destruction (usually same kind), I created some helper classes/interfaces that might make such attempts easier and I'd like to present them here to demonstrate what is achievable with API and current syntax features... Let's start with a code snippet that shows how this must be done without such helper classes today. A method that takes a path and a regular expression and returns a list of lines read from files found recursively below given path that contain at least one match of the regular expression: public static List<String> grep(Path dir, Pattern pattern) { try ( var paths = Files.find(dir, 100, (p, a) -> true) ) { return paths .filter(f -> Files.isRegularFile(f) && Files.isReadable(f)) .flatMap( f -> { BufferedReader br; try { br = Files.newBufferedReader(f, StandardCharsets.UTF_8); } catch (IOException e) { throw new UncheckedIOException(e); } return br.lines().onClose(() -> { try { br.close(); } catch (IOException e) { throw new UncheckedIOException(e); } }); } ) .filter(line -> pattern.matcher(line).find()) .collect(Collectors.toList()); } catch (IOException e) { throw new UncheckedIOException(e); } } This can be rewritten in a more functional style using helpers to: public static final Try<BaseStream<?, ?>, IOException> streamTry = new Try<>(BaseStream::close, UncheckedIOException::new); public static final Try<Closeable, IOException> ioTry = new Try<>(Closeable::close, UncheckedIOException::new); public static List<String> grep(Path dir, Pattern pattern) { return streamTry .with( () -> Files.find(dir, 100, (p, a) -> true) ) .applyAndDispose( paths -> paths .filter(f -> Files.isRegularFile(f) && Files.isReadable(f)) .flatMap( f -> ioTry.with(() -> Files.newBufferedReader(f, StandardCharsets.UTF_8)) .apply((br, dispose) -> br.lines().onClose(dispose)) ) .filter(line -> pattern.matcher(line).find()) .collect(Collectors.toList()) ); } Note that this helper is not limited to `AutoCloseable` resources. It captures resource destruction function and checked exception wrapping function in an instance of `Try` which provides two styles of handling resource lifecycle: `with(constructor).applyAndDispose(consumptor)` - construction, consumprion with automatic resource disposal after the `consumptor` function terminates but before `applyAndDispose` terminates `with(constructor).apply(consumptorAndDestructor)` - construction, consumption with arranged resource disposal by arranging to call the passed-in `Runnable` instance in the `consumptorAndDestructor` function itself. Both styles are demonstrated above. Here's the `Try` helper: https://gist.github.com/plevart/c26e9908573d4a28c709b7218b001ea8 Regards, Peter ------------- PR: https://git.openjdk.java.net/jdk/pull/5796