Questions about Stream/Iterable/Files - and possibly the compiler
Remi Forax
forax at univ-mlv.fr
Thu Nov 5 22:05:22 UTC 2015
----- Mail original -----
> De: "Fabrizio Giudici" <Fabrizio.Giudici at tidalwave.it>
> À: core-libs-dev at 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.giudici at tidalwave.it
>
More information about the core-libs-dev
mailing list