5

I wish to implement the Stream<E> interface (I admit, it's the unnecessarily large one) and add a builder method foo().

public MyStream<E> implements Stream<E>, ExtendedStream<E> {

    private final Stream<E> delegate;

    public MyStream(final Stream<E> stream) {
        this.delegate = stream;
    }

    // a sample Stream<E> method implementation
    @Override
    public <R> MyStream<R> map(Function<? super E, ? extends R> mapper) {
        return new MyStream<>(this.delegate.map(mapper));
    }
    // the rest in the same way (skipped)

    // a method from ExtendedStream<E>
    @Override
    public MyStream<E> foo() {
        return new MyStream(this.delegate.......);
    }  
}

So far so good.

long count = new MyStream(list.stream())
    .map(i -> i * 10)
    .foo()
    .filter(i -> i > 100)
    .count();

I have trouble with the Closeable behavior of Stream. The documentation of Stream says about closing (formatting mine):

Streams have a BaseStream.close() method and implement AutoCloseable, but nearly all stream instances do not actually need to be closed after use. Generally, only streams whose source is an IO channel (such as those returned by Files.lines(Path, Charset)) will require closing.

The only methods that close Stream are flatMap or close.

The instantiation of an object in Eclipse Oxygen is underlined with a warning:

Resource leak: '<unassigned Closeable value>' is never closed

This is not reproducible with IntelliJIdea 2018.1.5. Related questions I skimmed through are here and here. I understand the Closeable issues with File or Dictionary, however, I am stuck with Streams.

I dislike the static method MyStream.of(...) calling a private constructor workaround.

shmosel
  • 49,289
  • 6
  • 73
  • 138
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Have yo tryed giving to it the type parameter? new `MyStream(list.stream())`... – developer_hatch Dec 27 '18 at 18:52
  • @DamianLattenero: Rather `MyStream` since the origin is `List`. Yes, I did, no difference. – Nikolas Charalambidis Dec 27 '18 at 19:04
  • I had a similar issue, but in my case wasn't an implementation of the `Stream` interface. Rather, in TotalCross, there is access to the functional interfaces provided by Java 8 (but not default nor static methods there, just the abstract one) but nothing similar to `j.u.Stream`. Therefore, I've implemented my own version of `Stream` (project [here](https://gitlab.com/geosales-open-source/totalcross-functional-toolbox)) with the platform limitations. I've forgotten to add the `BaseStream` methods, so I just noticed now that there wasn't any `close` in my implementations... – Jefferson Quesado Feb 19 '20 at 14:31
  • 1
    To workaround this, my classes do not implements `AutoCloseable`, but declares a method `close`. Also created a `StreamHolder` class that receives a `Stream` and is autocloseable, calling the `Stream` close method and allowing to retrieve the original `Stream` (not yet pushed) – Jefferson Quesado Feb 19 '20 at 14:31
  • 1
    For the curious, I've pushed some work towards this goal: https://gitlab.com/geosales-open-source/totalcross-functional-toolbox/-/merge_requests/24 ; I'm brazilian and speak portuguese mainly, and my workmates also are portuguese-speaking brazilians, so my texts in the repo/code are mainly in portuguese – Jefferson Quesado Feb 19 '20 at 17:32

2 Answers2

8

As part of work on JSR 335, the JRE libraries evolved by introducing java.util.Stream and at the same time leveraging the new concept in places like java.nio. During this time the Eclipse team was consulted by the JSR 335 expert group, to discuss the following conflict:

  • Tools like ecj want to signal when programmers forget to close a GC-resistent (GCR) resource like, e.g., a FileInputStream.

  • The library team planned to make java.util.Stream a subtype of AutoCloseable to enable usage in try-with-resource, motivated by the fact that a j.u.Stream could potentially be backed by a GCR resource. Still, the default assumption should be that instances of j.u.Stream do not require a close() call.

  • Still, certain methods in java.nio returning j.u.Stream require to be close()d.

EG and Eclipse agreed, that no easy solution could be found such that just by looking at the type of closeable any tool could precisely recognize whether closing is necessary or not. This is due to intricacies of various resources wrapping other resources at several levels. In particular the type j.u.Stream gives no indication whether or not instances are backed by GCR resources or not.

For a clean solution, it was further mentioned, that a system of type annotations (using JSR 308) would be needed to enrich the type system with the information needed for precise static analysis. To the best of my knowledge, such approach hasn't materialized until today.

As a compromise, tool implementors like Eclipse were advised to encode heuristics along the following lines:

  • Normally, all instances of type AutoCloseable should be closed.

  • The following known set of types was to be excluded from the analysis, because those typically do not require closing: java.util.Stream and {Int,Long,Double}Stream.

  • As an exception from the exception, certain Stream-returning static methods in java.nio are known to require closing.

The discussion basically happened between the following two posts on the lambda-libs-spec-observers mailing list:

So much for the history.

The discussion in 2013 did not account for custom implementations of j.u.Stream. Eclipse assumes no specific knowledge about those implementations. It would be better, if not the tool would decide a bias towards needing / not needing close(), but if the implementor (here of MyStream) would have the means to indicate, whether instances of this class require closing or not. To-date implementors have, however, no means to express this.

For lack of a complete and precise option, we could discuss extending the set of heuristics, such that not only the known set of types in the j.u.Stream family, but also all its subtypes are excluded from the analysis, and considered to be GC-friendly. Obviously, such approach would increase the risk of false negatives (bugs missed by the analysis).

Marking warnings as "potential leaks", as suggested by howlger's answer would be confusing, because in flow-analysis, the word "potential" should normally indicate a behavior that occurs on some, but not all, flows through the program.

Available options as of today are:

  • Using @SuppressWarnings("resource") wherever MyStream is used (preferable)

  • Lowering the severity of this particular problem (if use of MyStream is too wide spread for using the first option).

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Stephan Herrmann
  • 7,963
  • 2
  • 27
  • 38
4

In Java 7 the description of AutoCloseable is

"...must be closed..."

whereas in Java 8 the description was semantically changed to

"...that may hold resources (such as file or socket handles)..."

In Eclipse the resource leak warning is shown independent of the Java version for all Closeable and AutoCloseable instances that are not being closed (which is the case in your example). See Eclipse help:

Classes implementing the interface java.io.Closeable (since JDK 1.5) and java.lang.AutoCloseable (since JDK 1.7) are considered to represent external resources, which should be closed using method close(), when they are no longer needed.

According to the changed Javadoc description, I would expect in Java 8 or higher for a not closed AutoCloseable only a Potential Resource Leak warning instead of a Resource Leak warning. Stephan Herrmann, an Eclipse JDT developer, explains in his answer why he doesn't think this is a good idea.

As a workaround for Java 8 or higher, add @SuppressWarnings("resource") to those places where the AutoCloseable does not have to be closed.

howlger
  • 31,050
  • 11
  • 59
  • 99