为什么消费者接受与陈述体而不是expression体的lambdas?

下面的代码令人惊讶的是编译成功:

Consumer<String> p = ""::equals; 

这个也是:

 p = s -> "".equals(s); 

但是,这是失败的错误boolean cannot be converted to void的预期:

 p = s -> true; 

用括号修改第二个例子也失败了:

 p = s -> ("".equals(s)); 

这是Java编译器中的错误还是有我不知道的types推理规则?

首先,值得看看Consumer<String>实际上是什么。 从文档 :

表示接受单个input参数且不返回结果的操作 。 与大多数其他function界面不同的是,消费者有望通过副作用进行操作。

所以这是一个函数,接受一个string,并没有返回。

 Consumer<String> p = ""::equals; 

编译成功,因为equals可以采取一个string(实际上,任何对象)。 等于的结果被忽略。*

 p = s -> "".equals(s); 

这完全一样,但语法不同。 编译器知道不要添加隐式return因为Consumer不应该返回一个值。 如果lambda是一个Function<String, Boolean>则会添加一个隐式return Function<String, Boolean>

 p = s -> true; 

这需要一个string,但是因为true是一个expression式而不是一个语句,所以结果不能以同样的方式被忽略。 编译器必须添加隐式return因为expression式不能独立存在。 因此,这确实有一个返回:一个布尔值。 所以它不是Consumer 。**

 p = s -> ("".equals(s)); 

再次,这是一个expression ,而不是一个声明。 忽略lamdbas一会儿,你会看到System.out.println("Hello"); 如果将其包含在括号中,将同样无法编译。


*从规格 :

如果一个lambda的主体是一个语句expression式(也就是说,一个expression式将被允许独立作为一个语句),它是兼容的void函数types; 任何结果都被简单地丢弃。

**从规范 (谢谢, 尤金 ):

一个lambdaexpression式与一个[void-producing]函数types一致,如果… lambda体是一个语句expression式( §14.8 )或一个void兼容块。

我认为其他答案通过关注lambda而使解释复杂化,而在这种情况下,它们的行为类似于手动实现的方法的行为。 这编译:

 new Consumer<String>() { @Override public void accept(final String s) { "".equals(s); } } 

而这不是:

 new Consumer<String>() { @Override public void accept(final String s) { true; } } 

因为"".equals(s)是一个陈述,但是true不是。 返回void的函数接口的lambdaexpression式需要一个语句,所以它遵循与方法正文相同的规则。

请注意,通常,lambda体不遵循与方法体完全相同的规则 – 特别是如果一个lambdaexpression式的body是一个expression式实现了一个返回值的方法,则它具有隐式return 。 因此,例如, x -> true将是Function<Object, Boolean>的有效实现,而true; 不是一个有效的方法体。 但在这种特殊情况下,function接口和方法体是一致的。

 s -> "".equals(s) 

 s -> true 

不要依赖相同的函数描述符。

s -> "".equals(s) String->void或者String->boolean函数描述符。
s -> true指的是只有String->boolean函数描述符。

为什么?

  • 当你写s -> "".equals(s) ,lambda: "".equals(s)的主体是一个产生一个值的语句
    编译器认为函数可能返回voidboolean

所以写作:

 Function<String, Boolean> function = s -> "".equals(s); Consumer<String> consumer = s -> "".equals(s); 

已validation。

当将lambda体指定给Consumer<String>声明的variables时,将使用描述符String->void
当然,这段代码没有什么意义(你检查相等,而不使用结果),但是编译器不关心。
当你写一个语句时,它是一样的: myObject.getMyProperty() getMyProperty()返回一个boolean值,但不存储结果。

  • 当你写s -> true ,lambda: true的主体是一个expression式
    编译器认为函数返回必然是 boolean
    所以只能使用描述符String->boolean

现在回到你的不能编译的代码。
你想做什么 ?

 Consumer<String> p = s -> true; 

你不能。 你想分配一个variables,使用函数描述符Consumer<String>带有String->void函数描述符的lambda体。 它不匹配!