Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Java, why can't I use a lambda as an enhanced for loop's Expression?

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?

like image 482
user4235730 Avatar asked Sep 10 '15 16:09

user4235730


People also ask

Can we use lambda expression for non functional interface?

Lambda expressions can operate only on functional interfaces (i.e. interface with only one abstract method).

Which is not a valid lambda expression in Java?

Answer: Only 4 and 5 are invalid lambdas. 1 . This lambda has no parameters and returns void.

Can lambda expressions be used for all interfaces?

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.

Is lambda more efficient than for loop?

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.


1 Answers

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.

like image 102
ZhongYu Avatar answered Sep 25 '22 23:09

ZhongYu