The Coordinator
The Coordinator

Reputation: 13137

Why is BufferedReader not closed when obtaining `Stream<String>` in try-with-resources?

The reader should be closed when a Stream is used in a try-with-resources.

Given this:

try(Stream<String> lines = new BufferedReader(reader).lines()) {
            return lines.map(it -> trim ? it.trim() : it)
                    .collect(Collectors.toList());
}

... the reader is not being closed??

This test fails:

    AtomicBoolean closed = new AtomicBoolean(false);

    Reader r = new StringReader("  Line1 \n Line2") {

                @Override
                public void close() {
                    super.close();
                    closed.set(true);
                }

            };

    try(Stream<String> lines = new BufferedReader(r).lines()) {
            lines.map(it -> trim ? it.trim() : it)
                    .collect(Collectors.toList());
    }

    assertTrue("Reader was not closed.",closed.get());

Upvotes: 4

Views: 1262

Answers (2)

bjmi
bjmi

Reputation: 603

Although it is not an immediate answer to your question it ensures that all resources are closed afterwards.

Inspired by Guava's CharSource.lines() implementation that uses a close handler Stream.onClose(Runnable) to close the corresponding BufferedReader when the processed stream was closed and
the characteristic of Stream.flatMap(...) (thanks to this trick)

to call BaseStream.close() after its contents have been placed into this stream.

you could get a stream of lines that will be autoclosed after a terminal operation.

Stream<String> lines = lines(reader);
// ..


Stream<String> lines(Reader reader) {
    BufferedReader br = new BufferedReader(reader);
    Stream<String> lines = br.lines()
            .onClose(() -> {
                try {
                    br.close();
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            });
    return Stream.of(lines).flatMap(s -> s); // Autocloseable exploit of Stream.flatMap()
}

Upvotes: 1

Adrian Shum
Adrian Shum

Reputation: 40056

I haven't actually used try-resources syntax. Wish my answer makes sense.

From my understanding, auto-close is closing the resource declared at the statement, nothing else.

Therefore, try(Stream<String> lines = new BufferedReader(r).lines()) { is simply closing lines, but not that buffered reader that have no variable assigned.

If you are intended to close both the buffered reader and the stream (do you really need to close the stream anyway?), iirc, you can have multiple lines in the try statement:

try (BufferedReader br = new BufferedReader(r);
     Stream<String> lines = br.lines()) {
    //....
}

somethings like that. (Haven't tried to compile that, wish it works :P)

Upvotes: 10

Related Questions