----- Mail original -----
> De: "Fabrizio Giudici" <fabrizio.giud...@tidalwave.it>
> À: core-libs-dev@openjdk.java.net
> Envoyé: Jeudi 5 Novembre 2015 22:12:53
> Objet: Questions about Stream/Iterable/Files - and possibly the compiler
> 
> Hello.

Hello Fabrizio,

> 
> My question is for the sake of curiosity, not being related to a real
> problem - or, better, the problem - which is tiny - can be fixed with a
> simple work around. But I'd like to blog a short post about it and I'd
> like to check I have all the context. It stemmed from a class about Java 8
> that I recently taught and one of the participants asked about that.
> 
> I think that there's something that possibly involves the compiler - I'll
> eventually post the relevant part of the questions to the proper mailing
> list.
> 
> 
> 1. Everything starts from this code chunk that doesnt' compile:
> 
>          Stream<String> s = IntStream.rangeClosed(1, 10) // just as an
> example to quickly create a Stream<String>
>                  .mapToObj(n -> "String #" + n);
> 
>          Files.write(Paths.get("/tmp/pippo.txt"), s);
> 
> error: no suitable method found for write(Path,Stream<String>)
>          Files.write(Paths.get("/tmp/pippo.txt"), s);
>      method Files.write(Path,byte[],OpenOption...) is not applicable
>        (argument mismatch; Stream<String> cannot be converted to byte[])
>      method Files.write(Path,Iterable<? extends
> CharSequence>,Charset,OpenOption...) is not applicable
>        (argument mismatch; Stream<String> cannot be converted to Iterable<?
> extends CharSequence>)
>      method Files.write(Path,Iterable<? extends
> CharSequence>,OpenOption...) is not applicable
>        (argument mismatch; Stream<String> cannot be converted to Iterable<?
> extends CharSequence>)
> 

as you said later, there is no overload that takes a Stream as parameter. 

> 
> 2. Variation.
> 
>          Files.write(Paths.get("/tmp/pippo.txt"), (Iterable<String>)s);
> 
> This gives:
> 
> Exception in thread "main" java.lang.ClassCastException:
> java.util.stream.IntPipeline$4 cannot be cast to java.lang.Iterable
>       at StreamIteratorExample.main(StreamIteratorExample.java:13)
> 
> Ok, so far it's the fact described here
> 
>       
> http://stackoverflow.com/questions/20129762/why-does-streamt-not-implement-iterablet
> 
> on why Stream doesn't implement Iterable.
> 
> Question A: Is the SO answer "because iterator() is usually supposed to be
> callable multiple times, while in a Stream it can't" correct?

iterator() is not a 'real' method of Stream, it's a kind of escape hatch that 
you can use if you have an API that takes an Iterator as parameter but it will 
be slow.

> 
> 
> 3. This is the known trick around the problem:
> 
>          final Iterable<String> i = s::iterator;
>          Files.write(Paths.get("/tmp/pippo.txt"), i);
> 
> It works and I think I understand why (Iterable has the same functional
> descriptor of Supplier<Iterator>, which is s::iterator, so they are
> compatible in assignment - right?).

yes,
Iterable is a functional interface, seen as () -> Iterator by the commpiler, 
s::iterator is compatible with () -> Iterator, so it works !
(BTW, final is not necessary here)

> 
> 
> 4. But at this point putting it into the same line gives compilation error:
> 
>          Files.write(Paths.get("/tmp/pippo.txt"), s::iterator);
> 
> error: no suitable method found for write(Path,s::iterator)
>          Files.write(Paths.get("/tmp/pippo.txt"), s::iterator);
>      method Files.write(Path,byte[],OpenOption...) is not applicable
>        (argument mismatch; Array is not a functional interface)
>      method Files.write(Path,Iterable<? extends
> CharSequence>,Charset,OpenOption...) is not applicable
>        (argument mismatch; bad return type in method reference
>            Iterator<String> cannot be converted to Iterator<CharSequence>)
>      method Files.write(Path,Iterable<? extends
> CharSequence>,OpenOption...) is not applicable
>        (argument mismatch; bad return type in method reference
>            Iterator<String> cannot be converted to Iterator<CharSequence>)

The conversion from a method reference to a functional interface doesn't work 
if the target type is parameterized by wildcard.
This is specified like this in the Java spec. I consider that this is a bug but 
this is actually what the spec says.

I hope we will be able to fix that in Java 10, but that just my opinion.

> 
> 
> 5. This at last works:
> 
>          Files.write(Paths.get("/tmp/pippo.txt"),
> (Iterable<String>)s::iterator);
> 
> I assume that the compiler can't autonomously infer that s::iterator is
> compatible with Iterable<String> so the cast is needed. I have a question
> about that (feature or bug?), but this is related to the compiler, so I
> think is OT here.

It's not a feature. It's what the spec says. As i said, i consider it as a bug.

> 
> At last, question B: Given all those premises, is there a specific reason
> for which Files.write() hasn't been overloaded with a version capable of
> accepting a Stream<String>? It would have been the perfect complement of
> Files.lines()

adding overloads with different interfaces is not a good idea, you can have a 
class that implements both interfaces (Stream and Iterable).

> 
> 
> Thanks.

cheers,
Rémi

> 
> --
> Fabrizio Giudici - Java Architect @ Tidalwave s.a.s.
> "We make Java work. Everywhere."
> http://tidalwave.it/fabrizio/blog - fabrizio.giud...@tidalwave.it
> 

Reply via email to