以下代码令人惊讶地成功编译:
Consumer<String> p = ""::equals;
这也是:
p = s -> "".equals(s);
但是失败了,错误boolean cannot be converted to void
正如预期的那样:
p = s -> true;
使用括号修改第二个示例也失败:
p = s -> ("".equals(s));
它是Java编译器中的错误还是我不知道的类型推断规则?
答案 0 :(得分:80)
首先,值得看一下Consumer<String>
实际上是什么。 From the documentation:
表示接受单个输入参数的操作 不返回结果。不像大多数其他功能接口,消费者 预计会通过副作用来运作。
所以它是一个接受String并且不返回任何内容的函数。
Consumer<String> p = ""::equals;
成功编译,因为equals
可以获取字符串(实际上是任何对象)。等于的结果只是被忽略了。*
p = s -> "".equals(s);
这完全相同,但语法不同。编译器知道不添加隐式return
,因为Consumer
不应返回值。如果lambda是return
,它将添加隐式Function<String, Boolean>
。
p = s -> true;
这需要一个String(s
),但因为true
是一个表达式而不是一个语句,所以不能以相同的方式忽略结果。编译器必须添加隐式return
,因为表达式本身不能存在。因此,这个 有一个返回:一个布尔值。因此,它不是Consumer
。**
p = s -> ("".equals(s));
同样,这是一个表达式,而不是一个语句。暂时忽略lambdas,如果用括号括起来,你会发现行System.out.println("Hello");
同样无法编译。
*来自the spec:
如果lambda的主体是一个语句表达式(即,允许独立作为语句的表达式),它与void生成函数类型兼容;任何结果都会被丢弃。
如果......,lambda表达式与[void-producing]函数类型一致 lambda body是一个语句表达式 (§14.8) 或与空白兼容的块。
答案 1 :(得分:11)
我认为其他答案通过关注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的函数接口的lambda表达式需要一个语句,因此它遵循与方法体相同的规则。
请注意,一般情况下,lambda实体不会完全遵循 与方法体相同的规则 - 特别是,如果一个lambda,其body是一个表达式,它实现了一个返回值的方法,它具有一个隐式return
。例如,x -> true
将是Function<Object, Boolean>
的有效实现,而true;
不是有效的方法体。但在这种特殊情况下,功能接口和方法体重合。
答案 2 :(得分:8)
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);
有效。
将lambda主体分配给Consumer<String>
声明的变量时,使用描述符String->void
。
当然,这段代码没有多大意义(你检查相等性而不使用结果)但是编译器并不关心。
在撰写陈述时也是如此:myObject.getMyProperty()
其中getMyProperty()
返回boolean
值但您不存储其结果。
s -> true
时,lambda的主体:true
是单个表达式。boolean
因此,只能使用描述符String->boolean
。现在,回到你不编译的代码
你想做什么?
Consumer<String> p = s -> true;
你做不到。您希望为使用函数描述符Consumer<String>
的变量分配一个带有String->void
函数描述符的lambda主体。
它不匹配!