[英]Why does this Java 8 lambda fail to compile?
以下Java代码无法编译:
@FunctionalInterface
private interface BiConsumer<A, B> {
void accept(A a, B b);
}
private static void takeBiConsumer(BiConsumer<String, String> bc) { }
public static void main(String[] args) {
takeBiConsumer((String s1, String s2) -> new String("hi")); // OK
takeBiConsumer((String s1, String s2) -> "hi"); // Error
}
编译器报告:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
奇怪的是,标记为“ OK”的行可以正常编译,但是标记为“ Error”的行失败。 它们看起来基本相同。
您的lambda需要与BiConsumer<String, String>
。 如果您参考JLS#15.27.3(Lambda的类型) :
如果满足以下所有条件,则lambda表达式与函数类型一致:
- [...]
- 如果函数类型的结果为void,则lambda主体为语句表达式(第14.8节)或与void兼容的块。
因此,lambda必须是语句表达式或与void兼容的块:
基本上, new String("hi")
是可执行的一段代码,实际上可以执行某项操作(它会创建一个新的String,然后将其返回)。 返回的值可以忽略, new String("hi")
仍可以在void返回lambda中使用,以创建新的String。
但是, "hi"
只是一个常量,它自己不会执行任何操作。 在lambda主体中与此有关的唯一合理的事情是将其返回 。 但是lambda方法必须具有返回类型String
或Object
,但它返回的是void
,因此String cannot be casted to void
错误。
第一种情况是可以的,因为您正在调用“特殊”方法(构造函数),而实际上并没有使用创建的对象。 为了更清楚一点,我将可选的花括号放在您的lambda中:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK
takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
更清楚地说,我将其转换为较旧的表示法:
takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
public void accept(String s, String s2) {
new String("hi"); // OK
}
});
takeBiConsumer(new BiConsumer<String, String>(String s1, String s2) {
public void accept(String s, String s2) {
"hi"; // Here, the compiler will attempt to add a "return"
// keyword before the "hi", but then it will fail
// with "compiler error ... bla bla ...
// java.lang.String cannot be converted to void"
}
});
在第一种情况下,您正在执行构造函数,但没有返回创建的对象,在第二种情况下,您试图返回String值,但是接口BiConsumer
方法返回void,因此出现编译器错误。
JLS指定
如果函数类型的结果为void,则lambda主体为语句表达式(第14.8节)或与void兼容的块。
现在让我们详细了解一下
由于您的takeBiConsumer
方法为void类型,因此接收new String("hi")
的lambda会将其解释为类似于
{
new String("hi");
}
这在void中有效,因此第一种情况可以编译。
但是,在lambda为-> "hi"
的情况下,例如
{
"hi";
}
在Java中不是有效的语法。 因此,与“ hi”有关的唯一事情就是尝试将其返回。
{
return "hi";
}
在无效的地方无效并解释错误消息
incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
为了更好地理解,请注意,如果将takeBiConsumer
的类型更改为String,则-> "hi"
将是有效的,因为它只会尝试直接返回字符串。
请注意,起初我强调该错误是由于lambda在错误的调用上下文中引起的,因此我将与社区分享这种可能性:
如果lambda表达式出现在程序中而不是赋值上下文(第5.2节),调用上下文(第5.3节)或强制转换上下文(第5.5节)中,则是编译时错误。
但是,就我们而言,我们处于正确的调用上下文中。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.