Assume we have an Iterator<Integer> iterator
. Because Iterable
is a functional interface, we can write:
Iterable<Integer> iterable = () -> iterator;
We can then of course use iterable
as an enhanced for loop's Expression:
for (Integer i : iterable) foo(i);
So why is
for (Integer i : () -> iterator) foo(i);
not allowed? (It results in the following compiler error:)
error: lambda expression not expected here for (Integer i : () -> iterator) foo(i); ^
Making the target type explicit like
for (Integer i : (Iterable<Integer>) () -> iterator) foo(i);
obviously works, but why can the compiler not infer the λ-expression's target type if it is omitted? From the fact that the Expression is in λ notation, should it not be clear to the compiler that the target type cannot be an Array
, and hence must be Iterable
?
Is this just an oversight by the language designers, or is there something else I am missing here?
Lambda expressions can operate only on functional interfaces (i.e. interface with only one abstract method).
Answer: Only 4 and 5 are invalid lambdas. 1 . This lambda has no parameters and returns void.
From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. A functional interface can have any number of default methods.
The answer is it depends. I have seen cases where using a lambda was slower and where it was faster. I have also seen that with newer updates you get more optimal code.
This is not just about lambda expression; it's about all poly expressions that require target typing.
One thing for sure is that this is not an oversight; the case was considered and rejected.
To quote an early spec :
http://cr.openjdk.java.net/~dlsmith/jsr335-0.9.3/D.html
Deciding what contexts are allowed to support poly expressions is driven in large part by the practical need for such features:
The expression in an enhanced for loop is not in a poly context because, as the construct is currently defined, it is as if the expression were a receiver:
exp.iterator()
(or, in the array case,exp[i]
). It is plausible that an Iterator could be wrapped as an Iterable in a for loop via a lambda expression (for (String s : () -> stringIterator)
), but this doesn't mesh very well with the semantics of Iterable.
My take is that, each invocation of Iterable.iterator()
must return a new, independent iterator, positioned at the beginning. Yet, the lambda expression in the example (and in your example) returns the same iterator. This does not conform to the semantics of Iterable
.
In any case, it seems unnecessary work to support target typing in for-each loop. If you already have the iterator, you can simply do
iterator.forEachRemaining( element->{ ... } )
Or if you prefer old-school
while(iterator.hasNext()) { Foo elment = iterator.next();
Neither are too bad; it's not worth complicating the language spec even more. (If we do want for-each to provide target typing, remember it needs to work for other poly expressions as well, like ?:
; then for-each can become too difficult to understand in some cases. And in general, there are two possible target types, Iterable<? extends X> | X[]
, which is very difficult for the type inference system.)
The for-each construct could be considered a syntax sugar because lambda wasn't available. If the language already has lambda expression, it is really unnecessary to have a special language construct to support for-each; it can be done by library APIs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With