As per my understanding of java 8 lambda expressions, if we don't include code after "->" in curly braces that value will be returned implicitly. But in case of below example, forEach
method expects Consumer
and expression returns value but the compiler is not giving an error in Eclipse.
List<StringBuilder> messages = Arrays.asList(new StringBuilder(), new StringBuilder());
messages.stream().forEach(s-> s.append("helloworld"));//works fine
messages.stream().forEach((StringBuilder s)-> s.append("helloworld")); //works fine
messages.stream().forEach(s-> s); // doesn't work , Void methods cannot return a value
messages.stream().forEach(s-> s.toString()); // works fine
messages.stream().forEach(s-> {return s.append("helloworld");}); // doesn't work , Void methods cannot return a value
messages.stream().forEach((StringBuilder s)-> {return s.append("helloworld");}); // doesn't work , Void methods cannot return a value
s.append
returns StringBuilder
and s.toString()
returns String
but lambda treats it as void
.
What am I missing here? Why isn't the compiler giving an error when we invoke method on object?
The forEach method was introduced in Java 8. It provides programmers a new, concise way of iterating over a collection. The forEach method performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.
The reason for the different results is that forEach() used directly on the list uses the custom iterator, while stream(). forEach() simply takes elements one by one from the list, ignoring the iterator.
Performance. There are many opinions about which style performs better. The short version basically is, if you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better.
forEach takes the collection's lock once and holds it across all the calls to the action method. The Stream. forEach call uses the collection's spliterator, which does not lock, and which relies on the prevailing rule of non-interference.
From JLS 15.27.3. Type of a Lambda Expression:
A lambda expression is congruent with a function type if all of the following are true:
The function type has no type parameters.
The number of lambda parameters is the same as the number of parameter types of the function type.
If the lambda expression is explicitly typed, its formal parameter types are the same as the parameter types of the function type.
If the lambda parameters are assumed to have the same types as the function type's parameter types, then:
If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.
If the function type's result is a (non-void) type R, then either i) the lambda body is an expression that is compatible with R in an assignment context, or ii) the lambda body is a value-compatible block, and each result expression (§15.27.2) is compatible with R in an assignment context.
The highlighted sentence above means that any statement lambda expression (i.e. a lambda expression without a block) matches a functional interface whose single method's return type is void
(such as the Consumer
functional interface required by the forEach
method).
This explains why s.append("helloworld")
& s.toString()
(your 1,2 & 4 examples) work fine as statement lambda expressions.
examples 5 & 6 don't work, since those have block lambda bodies which are value-compatible lambda expressions. To be void-compatible
, all the return statements must return nothing (i.e. just return;
).
On the other hand, the following void-compatible
block lambda bodies will pass compilation :
messages.stream().forEach(s-> {s.append("helloworld");});
messages.stream().forEach(s-> {s.append("helloworld"); return;});
Your 4th example - messages.stream().forEach(s-> s);
doesn't work for the same reason the following method doesn't pass compilation :
void method (StringBuilder s)
{
s;
}
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