Stream is AutoCloseable, Stream.onClose

Paul Sandoz paul.sandoz at
Thu Jun 20 08:17:11 PDT 2013


Having another go at this...

The current solution of resource management for Stream is poor (as previously mentioned):

1) CloseableStream/DelegatingStream add a lot of API surface area.

2) Operations on CloseableStream do not return a CloseableStream.

3) try-with-resources always needs to be used with the CloseableStream since the Stream itself has no closeable semantics.

4) Stream.concat fails to propagate the closing

If Stream extends from AutoCloseable we can address the first 3 issues. In fact it can be addressed with just a close method but it is awkward to transform that into an AutoCloseable for use with try-with-resources: try (AutoCloseable ac = () -> s.close()) { … }

A negative point is it is no longer clear whether a stream should be closed or not, which is anyway the case for issues 2/3/4. However, i don't think that should stop us trying to improve the general situation, it's not gonna be perfect but i think we can do better than what we currently have [*].

Issue 4 can be addressed by adding a Stream.onClose(AutoCloseable ac) method.

Stream s = ...
s = s.onClose(a).filter(...).onClose(b).
s.close(); // b is called, then a is called
s.toArray(); // throws ISE

Stream s = ...
s = s.onClose(a).onClose(b).
s.close(); // b is called, then a is called
s.toArray(); // throws ISE

The Stream.concat implementation becomes:

        Stream<T> cs = (a.isParallel() || b.isParallel())
               ? StreamSupport.parallelStream(split)

        return cs.onClose(() -> { a.close(); b.close(); } ) // ignoring exception handling to be brief

The Stream.close/onClose methods enable us to specify more precisely the behaviour of Stream.close, the order in which calls to close on AutoClosable instances passed to onClose occur, and what happens if AutoCloseable.close throws an exception.

Of course it is possible to do silly things like this:

  s = s.conClose(a).filter(...).onClose(s).

but we could detect if s is stage in the pipeline and throw an IAE.

FWIW Stream.close/onClose is easy to implement efficiently.


[*] The JDK world is murky, see ByteArrayInputStream.close:

     * Closing a <tt>ByteArrayInputStream</tt> has no effect. The methods in
     * this class can be called after the stream has been closed without
     * generating an <tt>IOException</tt>.
     * <p>
    public void close() throws IOException {

More information about the lambda-libs-spec-experts mailing list