为什么这个 Java 8 lambda 无法编译?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29262002/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does this Java 8 lambda fail to compile?
提问by Brian Gordon
The following Java code fails 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
}
The compiler reports:
编译器报告:
Error:(31, 58) java: incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
The weird thing is that the line marked "OK" compiles fine, but the line marked "Error" fails. They seem essentially identical.
奇怪的是,标记为“OK”的行编译正常,但标记为“Error”的行失败。它们看起来基本相同。
采纳答案by assylias
Your lambda needs to be congruent with BiConsumer<String, String>
. If you refer to JLS #15.27.3 (Type of a Lambda):
您的 lambda 需要与BiConsumer<String, String>
. 如果您参考JLS #15.27.3(Lambda 的类型):
A lambda expression is congruent with a function type if all of the following are true:
- [...]
- If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.
如果以下所有条件都为真,则 lambda 表达式与函数类型一致:
- [...]
- 如果函数类型的结果为 void,则 lambda 主体是语句表达式(第 14.8 节)或与 void 兼容的块。
So the lambda must either be a statement expression or a void compatible block:
因此 lambda 必须是语句表达式或 void 兼容块:
- A constructor invocation is a statement expressionso it compiles.
- A string literal isn't a statement expression and is not void compatible (cf. the examples in 15.27.2) so it does not compile.
- 构造函数调用是一个语句表达式,因此它可以编译。
- 字符串文字不是语句表达式,也不兼容 void(参见15.27.2 中的示例),因此无法编译。
回答by Jean-Fran?ois Savard
The JLS specify that
JLS 规定
If the function type's result is void, the lambda body is either a statement expression (§14.8) or a void-compatible block.
如果函数类型的结果为 void,则 lambda 主体是语句表达式(第 14.8 节)或与 void 兼容的块。
Now let's see that in detail,
现在让我们详细看看,
Since your takeBiConsumer
method is of void type, the lambda receiving new String("hi")
will interpret it as a block like
由于您的takeBiConsumer
方法是 void 类型,因此 lambda 接收new String("hi")
会将其解释为一个块,例如
{
new String("hi");
}
which is valid in a void, hence the first case compile.
这在无效中有效,因此第一种情况编译。
However, in the case where the lambda is -> "hi"
, a block such as
但是,在 lambda 为 的情况下,-> "hi"
诸如
{
"hi";
}
is not valid syntax in java. Therefore the only thing to do with "hi" is to try and return it.
在 java 中不是有效的语法。因此,与“嗨”有关的唯一事情就是尝试返回它。
{
return "hi";
}
which is not valid in a void and explain the error message
这在无效中无效并解释错误消息
incompatible types: bad return type in lambda expression
java.lang.String cannot be converted to void
For a better understanding, note that if you change the type of takeBiConsumer
to a String, -> "hi"
will be valid as it will simply try to directly return the string.
为了更好地理解,请注意,如果您将 的类型更改takeBiConsumer
为字符串,-> "hi"
将是有效的,因为它只会尝试直接返回字符串。
Note that at first I tought the error was caused by the lambda being in a wrong invocation context, so I'll share this possibility with the community :
请注意,起初我认为错误是由 lambda 处于错误的调用上下文中引起的,因此我将与社区分享这种可能性:
It is a compile-time error if a lambda expression occurs in a program in someplace other than an assignment context (§5.2), an invocation context (§5.3), or a casting context (§5.5).
如果 lambda 表达式出现在程序中的某个位置而不是赋值上下文(第 5.2 节)、调用上下文(第 5.3 节)或强制转换上下文(第 5.5 节),则会导致编译时错误。
However in our case, we are in an invocation contextwhich is correct.
然而,在我们的例子中,我们处于一个正确的调用上下文中。
回答by morgano
The first case is ok because you are invoking a "special" method (a constructor) and you are no actually taking the created object. Just to make it more clear, I'll put the optional braces in your lambdas:
第一种情况是可以的,因为您正在调用“特殊”方法(构造函数)并且您实际上并没有获取创建的对象。为了更清楚,我将把可选的大括号放在你的 lambda 表达式中:
takeBiConsumer((String s1, String s2) -> {new String("hi");}); // OK
takeBiConsumer((String s1, String s2) -> {"hi"}); // Error
And more clear, I'll translate that to the older notation:
更清楚的是,我将把它翻译成旧的符号:
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"
}
});
In the first case you are executing a constructor, but you are NOT returning the created object, in the second case you are attempting to return a String value, but your method in your interface BiConsumer
returns void, hence the compiler error.
在第一种情况下,您正在执行构造函数,但没有返回创建的对象,在第二种情况下,您尝试返回 String 值,但接口中的方法BiConsumer
返回 void,因此编译器错误。
回答by kajacx
Basicly, new String("hi")
is an executable piece of code that actually does something (it creates a new String and then returns it). The returned value can be ignored and new String("hi")
can still be used in void-return lambda to create a new String.
基本上,new String("hi")
是一段实际执行某些操作的可执行代码(它创建一个新字符串,然后返回它)。返回值可以被忽略,new String("hi")
并且仍然可以在 void-return lambda 中使用以创建一个新的 String。
However, "hi"
is just a constant that doesn't do anything on it's own. The only reasonable thing to do with it in lambda body is to returnit. But the lambda method would have to have return type String
or Object
, but it returns void
, hence the String cannot be casted to void
error.
然而,"hi"
它只是一个本身不做任何事情的常量。在 lambda 体中唯一合理的做法是返回它。但是 lambda 方法必须具有返回类型String
or Object
,但它返回void
,因此出现String cannot be casted to void
错误。