Proposal: JDK-8148917 Enhanced-For Statement Should Allow Streams

Peter Levart peter.levart at gmail.com
Thu Mar 14 10:20:40 UTC 2019


Hi Stuart,

The Alternatives section of the proposal is very thorough and it 
mentions the following alternative:

"
     An alternative adapter is to add a default method to Stream:

     default Iterable<T> asIterable() { return this::iterator; }

     for (T t : asIterable(stream)) {
         ...
     }

     This is slightly better at the call site, and it’s restricted to 
streams. But it’s still not as nice as IterableOnce and it facilitates 
creation of poorly-behaved Iterable instances.
"

It think this alternative is not given fair comparison. 1st this is an 
instance method, so the foreach loop should read:

for (T t : stream.asIterable()) {
     ...
}

...which is better in particular when creation of Stream and consumption 
is performed in single expression (not a good example, since it doesn't 
close the stream):

for (String line : Files.lines(path).asIterable()) {
     ...
}

In addition, why should this be restricted to streams? This could be 
combined with IterableOnce. The method could be called differently and 
specified as BaseStream's terminal operation, returning IterableOnce<T> 
instead of Iterable<T>:

     /**
      * Returns an {@link IterableOnce} for elements of this stream.
      *
      * <p>This is a <a href="package-summary.html#StreamOps">terminal
      * operation</a>.
      *
      * @return iterable for elements of this stream that may be 
iterated only once
      */
     default IterableOnce<T> iterate() {
         Iterator<T> iterator = iterator();
         return () -> iterator; // this should be a proper IterableOnce 
with exception thrown etc.
     }

So IntStream and friends would get this too, although with necessary 
boxing, which should be easy for JIT to eliminate and without conflicts 
between IntStream.forEach and Iterable.forEach.


What do you think? Is this additional method invocation too much of a 
drawback?


And now for something more controversial...

Is changing the language really out of the picture here?

The Iterator interface has got a new default method called 
forEachRemaining in JDK 8. This method could be seen roughly equivalent 
to one-shot iteration directly from IterableOnce:

IterableOnce.forEach() vs. IterableOnce.iterator().forEachReamining()

Both of above can only be performed once. And once is enough for foreach 
loop. So if foreach loop was retrofitted to also act on Iterator(s) as a 
possible right hand side (arrays, Iterable(s), Iterator(s)), then we 
might not need this IterableOnce at all. You could then just write:

for (T t : stream.iterator()) ...

But that's probably not going to happen right?


Regards, Peter


On 3/12/19 10:59 PM, Stuart Marks wrote:
> Hi Stephen,
>
>> My slight concern is that the terminal operation is hidden and not
>> immediately visible, which could be surprising. I do note that streams
>> throw a clear exception if a terminal operation is called a second
>> time which mitigates this to some degree.
>
> Yes, this is certainly a possibility.
>
> I'll note that even though my example does it, I think that storing a 
> stream in a local variable is a bit of a code smell. It has to be done 
> for try-with-resources, but storing the head of a pipeline in a local 
> variable so that it can be iterated over does introduce the 
> possibility of accidental reuse.
>
> I suspect that most cases (aside from try-with-resources) will call a 
> method returning a fresh Stream that's then iterated over. For example,
>
>     for (var x : getSomeStream()) {
>        // ...
>     }
>
> In this case there's no possibility of accidental reuse.
>
>> The IterableOnce class-level Javadoc contradicts the method-level
>> Javadoc. The class-level say "It is recommended that" whereas the
>> method-level Javadoc mandates.
>
> Oh yes, thanks. I'll fix this to make the behavior mandatory.
>
> s'marks
>



More information about the core-libs-dev mailing list