问题
Why doesn't forEach method in java not show a compiler error when a Function type argument is passed instead of Consumer? Here both of the lines are returning a boolean value for every element in the stream but only 2nd line gets a compilation error? Is there any other property of lambda expression for such scenario?
Here's my code:
Stream.of(1,2,3,4).forEach(a->a.equals(1));//line 1
Stream.of(1,2,3,4).forEach(a->{return a.equals(1);});//line 2
回答1:
Regarding why the first line works: there's an explanation in the specification:
Generally speaking, a lambda of the form
() -> expr
, whereexpr
is a statement expression, is interpreted as either() -> { return expr; }
or() -> { expr; }
, depending on the target type.
The above comes with the following example (good coincidence, this is very similar to your example):
// Consumer has a void result
java.util.function.Consumer<String> c = s -> list.add(s);
This simply means that the compiler ignores the return type for the expression, as though your code were simply this (which is valid for a void method):
Stream.of(1, 2, 3, 4).forEach(a -> {
a.equals(1);
});
And regarding the second line, the spec says:
A block lambda body is void-compatible if every return statement in the block has the form
return;
.
In your case, though, {return a.equals(1);}
does not meet this rule. Void methods don't return a value.
An easy way to understand this is to consider that the compiler applies method body validation rules (such that the body must be compatible with the declaration public void accept(T t)
) - as mentioned in the tutorial
回答2:
In the first line you provided a valid Consumer
which takes an argument int a
. This is what Stream.forEach
expects. The fact, that the consumer will additionaly return a value doesn't matter. The returned value will not be evaluated, it will be discarded.
The second line contains a statement which returns a boolean
. As this statement has a return value, it doesn't comply with the method of Consumer
which takes an argument but is declared void
. Thus this gives the compile-time error: Void methods cannot return a value
JLS 15.27.2 says:
A block lambda body is void-compatible if every return statement in the block has the form return;.
Thus the method returns without an explicit return value.
Regarding the return statement JLS 14.17. says:
A return statement with no Expression must be contained in one of the following, or a compile-time error occurs:
- A method that is declared, using the keyword void, not to return a value (§8.4.5)
Applying this to the second line results in the following code:
Stream.of(1, 2, 3, 4).forEach(a -> { a.equals(1); return; });
Further notes:
By returning the value of equals
in the second line this statement doesn't become a Function
. It's still just a statement.
An exception will not be thrown as an exception can only be thrown on runtime. Since the code doesn't compile, it cannot be executed. As specified in the JLS a compile-time error is issued.
回答3:
In fact, in the first line you have Consumer as returned value is ignored. But in the second line you return result explicitly so expression became type of Function.
来源:https://stackoverflow.com/questions/52474643/why-doesnt-for-each-method-in-java-not-throw-an-exception-when-a-function-type