The following Java code fails to compile:
@FunctionalInterface
private interface BiConsumer<A, B> {
void accept(A a, B b);
}
private static void takeBiConsumer(BiConsumer<String, String> bc) { }
public static void main(String[] args) {
takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
takeBiConsumer((String s1, String s2) -> "hi"); // Error
}
The compiler reports:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
The weird thing is that the line marked "OK" compiles fine, but the line marked "Error" fails. They seem essentially identical.
The first case is ok because you are invoking a "special" method (a constructor) and you are no actually taking the created object. Just to make it more clear, I'll put the optional braces in your lambdas:
And more clear, I'll translate that to the older notation:
In the first case you are executing a constructor, but you are NOT returning the created object, in the second case you are attempting to return a String value, but your method in your interface
BiConsumer
returns void, hence the compiler error.Your lambda needs to be congruent with
BiConsumer<String, String>
. If you refer to JLS #15.27.3 (Type of a Lambda):So the lambda must either be a statement expression or a void compatible block:
The JLS specify that
Now let's see that in detail,
Since your
takeBiConsumer
method is of void type, the lambda receivingnew String("hi")
will interpret it as a block likewhich is valid in a void, hence the first case compile.
However, in the case where the lambda is
-> "hi"
, a block such asis not valid syntax in java. Therefore the only thing to do with "hi" is to try and return it.
which is not valid in a void and explain the error message
For a better understanding, note that if you change the type of
takeBiConsumer
to a String,-> "hi"
will be valid as it will simply try to directly return the string.Note that at first I tought the error was caused by the lambda being in a wrong invocation context, so I'll share this possibility with the community :
JLS 15.27
However in our case, we are in an invocation context which is correct.
Basicly,
new String("hi")
is an executable piece of code that actually does something (it creates a new String and then returns it). The returned value can be ignored andnew String("hi")
can still be used in void-return lambda to create a new String.However,
"hi"
is just a constant that doesn't do anything on it's own. The only reasonable thing to do with it in lambda body is to return it. But the lambda method would have to have return typeString
orObject
, but it returnsvoid
, hence theString cannot be casted to void
error.