On Sun, 3 Oct 2021 11:00:25 GMT, Tagir F. Valeev <[email protected]> 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