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
函数描述符

为什么?

  • 当您编写
    s->“”.equals(s)
    时,lambda的主体:
    “”.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”)也同样无法编译


*发件人:

如果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;
    }
}