Java 为什么消费者接受带有语句体而非表达式体的lambda?
以下代码正在成功编译:Java 为什么消费者接受带有语句体而非表达式体的lambda?,java,lambda,java-8,type-inference,Java,Lambda,Java 8,Type Inference,以下代码正在成功编译: Consumer<String> p = ""::equals; 但此操作失败,出现错误布尔值无法按预期转换为void: p = s -> true; 用括号修改第二个示例也失败: p = s -> ("".equals(s)); 这是Java编译器中的一个bug还是有一个我不知道的类型推断规则 s -> "".equals(s) 及 不要依赖相同的函数描述符。 s->“”。equals可以指String->void或String->
Consumer<String> p = ""::equals;
但此操作失败,出现错误布尔值无法按预期转换为void
:
p = s -> true;
用括号修改第二个示例也失败:
p = s -> ("".equals(s));
这是Java编译器中的一个bug还是有一个我不知道的类型推断规则
s -> "".equals(s)
及
不要依赖相同的函数描述符。s->“”。equals
可以指String->void
或String->boolean
函数描述符。s->true
仅指String->boolean
函数描述符
为什么?- 当您编写
时,lambda的主体:s->“”.equals(s)
是一条生成值的语句“”.equals(s)
编译器认为函数可能返回
或void
boolean
Function<String, Boolean> function = s -> "".equals(s);
Consumer<String> consumer = s -> "".equals(s);
你不能。您希望将lambda主体与字符串->void
函数描述符一起分配给使用函数描述符使用者的变量。
不匹配 首先,值得一看消费者实际上是什么:
表示一个接受单个输入参数并
不返回结果。与大多数其他功能接口不同,消费者
预计将通过副作用发挥作用
所以它是一个接受字符串而不返回任何内容的函数
Consumer<String> p = ""::equals;
这完全相同,但语法不同。编译器知道不要添加隐式返回
,因为使用者
不应返回值。如果lambda是一个函数,那么它将添加一个隐式返回
p = s -> true;
这需要一个字符串(s
),但由于true
是一个表达式而不是一个语句,因此不能以同样的方式忽略结果。编译器必须添加一个隐式的返回值
,因为表达式本身不能存在。因此,这确实有一个返回:布尔值。因此,它不是消费者
**
p = s -> ("".equals(s));
同样,这是一个表达式,而不是一个语句。暂时忽略lambdas,您将看到行System.out.println(“Hello”)如果用括号括起来,code>也同样无法编译
*发件人:
如果lambda的主体是一个语句表达式(即允许作为语句独立的表达式),则它与生成void的函数类型兼容;任何结果都将被丢弃
**来自(谢谢,):
lambda表达式与[void-producting]函数类型一致,如果。。。
lambda主体是语句表达式或
()
或者是一个空心兼容块
我认为其他答案通过关注lambda使解释复杂化,而它们在本例中的行为类似于手动实现方法的行为。这包括:
new Consumer<String>() {
@Override
public void accept(final String s) {
"".equals(s);
}
}
新消费者(){
@凌驾
公共作废接受(最终字符串s){
“.等于;
}
}
鉴于此不:
new Consumer<String>() {
@Override
public void accept(final String s) {
true;
}
}
新消费者(){
@凌驾
公共作废接受(最终字符串s){
是的;
}
}
因为“.equals(s)
是一个语句,而true
不是。返回void的函数接口的lambda表达式需要一个语句,因此它遵循与方法体相同的规则
请注意,一般情况下,lambda实体并不遵循与方法实体完全相同的规则-特别是,如果其主体是表达式的lambda实现了返回值的方法,则它具有隐式的返回。例如,x->true
将是函数
的有效实现,而true代码>不是有效的方法体。但是在这种特殊情况下,函数接口和方法体是一致的。Eclipse也会抱怨,所以可能不是编译器错误。s->true
和s->(“”.equals(s))
被解释为s->{return true;}
或s->{return(“.equals(s));}
,它们不是函数<代码>p=s->true代码>这没有意义,使用p=s->{}代码>相反。@DavidPérezCabrera我理解为什么最后两行代码没有编译。它不清楚,因为后两个工作。@Zefick IMHO它是编译器的许可证,它必须解释您可以忽略返回值。为了避免不必要的冗长,如果它不这样做,您就不能使用p=”“::equals代码>并且您必须定义p=s->“”.equals代码>如下:p=s->{“。等于(s);}代码>。我认为这是一个好决定。看看吧。关于圆括号为什么会改变行为,请参见……因此关键时刻是可以忽略等于的结果。对于这样的强类型语言,这是出乎意料的。@Zefick是的,关键的区别在于语句可能有也可能没有隐式的返回(基于推断),而表达式总是有隐式的返回。“有一天,我想知道为什么语言中的语句和表达式之间有区别。”。我真的不明白,就让一切都变成语句吧…@Federicoperaltachaffner定义它的不是编译器,而是JLS:)(编译器必须遵守它)。如果所有表达式都包含语句,编译器将无法报告大量错误。如果必须使用非void函数的所有结果,则需要大量不必要的赋值/额外的局部变量,首先考虑StringBuilder.append(…)
。很长一段时间以来都是这样,可能是因为Java1.0“lambdas在这里没有什么特别之处”。不过,您基本上是对的。s->”。equals(s)
可以同时满足消费者
和功能
,我认为
p = s -> true;
p = s -> ("".equals(s));
new Consumer<String>() {
@Override
public void accept(final String s) {
"".equals(s);
}
}
new Consumer<String>() {
@Override
public void accept(final String s) {
true;
}
}