The difference is as follows:
Lambda expression looks like
parameters -> expression
or
parameters -> { block }
where either block returns a value - or it is not suitable for void behavior.
In other words, a parameters -> expression lambda is equivalent to parameters -> { return expression; } parameters -> { return expression; } if expression has a non-empty type or parameters -> { expression; } parameters -> { expression; } if expression is of type void (for example, System.out.printf() ).
Your first version essentially uses an expression with a bit of overhead:
i -> i = i * 2 can be reduced to i -> i * 2 , since the purpose of i = simply not necessary, because i immediately disappears without being used further.
It looks like
Integer function(Integer i) { i = i * 2; return i; }
or
Integer function(Integer i) { return (i = i * 2); }
which can be simplified to
Integer function(Integer i) { return i * 2; }
All these examples correspond to the UnaryOperator<Integer> interface, which is a special case of Function<Integer, Integer> .
On the contrary, you second example is similar to
XY function(int i) { i = i * 2; }
which does not work:
- Or
XY is void (which would make a Consumer<Integer> that doesn't match .map() ) - Or
XY really Integer (then the return statement is missing).
Where is the need for this value (except that the code is compiled)?
Well, .forEach(System.out::println); needs this value ...
So, everything that can be converted to Function<T, R> can be transferred to the stream .map() stream T , resulting in stream R :
Stream.of(1, 2, 3).map(i -> i * 2) Stream.of(1, 2, 3).map(i -> { return i * 2; })
rotate the Integer , which you pass into another Integer , giving you another Stream<Integer> . Did you notice that they are in the box?
Other methods:
All of these examples have a common meaning that they receive a value and produce a value of one or another type. (Note how these examples use AutoBoxing and AutoUnboxing as needed.)
OTOH, everything that can be converted to Consumer<T> can be passed to the .map() stream of stream T , which can be any form of lambda that expresses the void expression:
.forEach(x -> System.out.println(x)) .forEach(x -> { System.out.println(x); })
With this in mind, it is easy to see that lambda with the void expression type cannot be assigned to .map() , and lambda with the void type cannot be assigned to forEach() .