I came across some clever code to convert an Iterator to a Stream from Karol on this post. I have to admit that I don't completely understand how the lambda is allowed to be assigned to the Iterable
type in the following code...
static <T> Stream<T> iteratorToFiniteStream(final Iterator<T> iterator) {
final Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), false);
}
I decided to write my own small test to ensure that it compiles and executes, and it does.
public void printsStream_givenIterator()
{
Iterator<String> iterator = Arrays.asList("a", "b", "c").iterator();
final Iterable<String> iterable = () -> iterator;
StreamSupport.stream(iterable.spliterator(), false).forEach(s -> System.out.println(s));
}
// prints: abc
My understanding is that the lambda () -> iterator
is acting as a Supplier function.
Iterable isn't a FunctionalInterface so how can it be assigned this lambda?
() -> iterator
is not “acting as aSupplier
function”. Lambda expressions can be assigned to any congruent functional interface type. And there is no requirement for a functional interface to be annotated with@FunctionalInterface
. You can create anIterable
with a lambda expression, which will implement theIterable.iterator()
method, which is the onlyabstract
method of that interface. However, implementing it by returning the sameIterator
instance each time may violate the expectation of being able to iterate this object multiple times (which is the reason whyStream
doesn’t implementIterable
despite having aniterator()
method).This solution will work in this narrow context, but isn’t clever.
The sequence
just introduces an additional step before
Spliterators.spliteratorUnknownSize(iterator, 0)
, which is how thedefault
method ofIterable.spliterator()
is implemented.So
is just a less efficient variant of
If you compare this to the accepted answer of the Q&A you’ve linked, you’ll see that it is exactly that, but that answer takes the opportunity of passing specific
characteristics
instead of0
.As you've pointed out, assignment from a lambda expression is only valid if the target is a functional interface. This is described in section 15.27.3 of the JLS: Type of a Lambda Expression.
Jumping to section 9.8: Functional Interfaces, we can see the definition of a functional interface.
Iterable
does satisfy the criteria for a functional interface, because it has only one abstract method:iterator()
. (The 2 additionaldefault
methods do not violate the criteria, because they are not abstract.) Therefore, the assignment is valid.