The following code surprisingly is compiling successfully:
Consumer<String> p = ""::equals;
This too:
p = s -> "".equals(s);
But this is fails with the error boolean cannot be converted to void
as expected:
p = s -> true;
Modification of the second example with parenthesis also fails:
p = s -> ("".equals(s));
Is it a bug in Java compiler or is there a type inference rule I don't know about?
I think the other answers complicate the explanation by focusing on lambdas whereas their behavior in this case is similar to the behavior of manually implemented methods. This compiles:
whereas this does not:
because
"".equals(s)
is a statement buttrue
is not. A lambda expression for a functional interface returning void requires a statement so it follows the same rules as a method's body.Note that in general lambda bodies don't follow exactly the same rules as method bodies - in particular, if a lambda whose body is an expression implements a method returning a value, it has an implicit
return
. So for example,x -> true
would be a valid implementation ofFunction<Object, Boolean>
, whereastrue;
is not a valid method body. But in this particular case functional interfaces and method bodies coincide.and
don't rely on same function descriptors.
s -> "".equals(s)
may refer eitherString->void
orString->boolean
function descriptor.s -> true
refers to onlyString->boolean
function descriptor.Why ?
s -> "".equals(s)
, the body of the lambda :"".equals(s)
is a statement that produces a value.The compiler considers that the function may return either
void
orboolean
.So writing :
is valid.
When you assign the lambda body to a
Consumer<String>
declared variable, the descriptorString->void
is used.Of course, this code doesn't make much sense (you check the equality and you don't use the result) but the compiler doesn't care.
It is the same thing when you write a statement :
myObject.getMyProperty()
wheregetMyProperty()
returns aboolean
value but that you don't store the result of it.s -> true
, the body of the lambda :true
is a single expression .The compiler considers that the function returns necessarily
boolean
.So only the descriptor
String->boolean
may be used.Now, come back to your code that doesn't compile.
What are you trying to do ?
You cannot. You want to assign to a variable that uses the function descriptor
Consumer<String>
a lambda body with theString->void
function descriptor. It doesn't match !First, it's worth looking at what a
Consumer<String>
actually is. From the documentation:So it's a function that accepts a String and returns nothing.
Compiles successfully because
equals
can take a String (and, indeed, any Object). The result of equals is just ignored.*This is exactly the same, but with different syntax. The compiler knows not to add an implicit
return
because aConsumer
should not return a value. It would add an implicitreturn
if the lambda was aFunction<String, Boolean>
though.This takes a String (
s
) but becausetrue
is an expression and not a statement, the result cannot be ignored in the same way. The compiler has to add an implicitreturn
because an expression can't exist on its own. Thus, this does have a return: a boolean. Therefore it's not aConsumer
.**Again, this is an expression, not a statement. Ignoring lambdas for a moment, you will see the line
System.out.println("Hello");
will similarly fail to compile if you wrap it in parentheses.*From the spec:
**From the spec (thanks, Eugene):