----- 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 >