为什么消费者接受与陈述体而不是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)
的主体是一个产生一个值的语句 。
编译器认为函数可能返回void
或boolean
。
所以写作:
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体。 它不匹配!