为什么这个Java 8 lambda无法编译?

时间:2022-07-16 09:44:46

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”的行失败了。他们看起来基本相同。

4 个解决方案

#1


98  

Your lambda needs to be congruent with BiConsumer<String, String>. If you refer to JLS #15.27.3 (Type of a Lambda):

你的lambda需要与BiConsumer 一致。如果您参考JLS#15.27.3(Lambda的类型): ,string>

A lambda expression is congruent with a function type if all of the following are true:

如果满足以下所有条件,则lambda表达式与函数类型一致:

  • [...]
  • [...]
  • 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兼容的块。

So the lambda must either be a statement expression or a void compatible block:

因此lambda必须是语句表达式或void兼容块:

  • A constructor invocation is a statement expression so 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中的示例),因此它不能编译。

#2


42  

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”)是一段可执行的代码,它实际上做了某些事情(它创建了一个新的String然后返回它)。可以忽略返回的值,并且仍可以在void-return lambda中使用新的String(“hi”)来创建新的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 return it. 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或Object,但它返回void,因此String无法转换为void error。

#3


21  

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,因此编译器错误。

#4


11  

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类型,接收新String(“hi”)的lambda会将它解释为类似块

{
    new String("hi");
}

which is valid in a void, hence the first case compile.

这在void中是有效的,因此第一种情况是编译。

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中是无效的语法。因此,“hi”唯一要做的就是尝试归还它。

{
    return "hi";
}

which is not valid in a void and explain the error message

在void中无效并解释错误消息

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的类型更改为String,则 - >“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处于错误的调用上下文引起的,所以我将与社区分享这种可能性:

JLS 15.27

JLS 15.27

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 context which is correct.

但是在我们的例子中,我们处于一个正确的调用上下文中。

#1


98  

Your lambda needs to be congruent with BiConsumer<String, String>. If you refer to JLS #15.27.3 (Type of a Lambda):

你的lambda需要与BiConsumer 一致。如果您参考JLS#15.27.3(Lambda的类型): ,string>

A lambda expression is congruent with a function type if all of the following are true:

如果满足以下所有条件,则lambda表达式与函数类型一致:

  • [...]
  • [...]
  • 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兼容的块。

So the lambda must either be a statement expression or a void compatible block:

因此lambda必须是语句表达式或void兼容块:

  • A constructor invocation is a statement expression so 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中的示例),因此它不能编译。

#2


42  

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”)是一段可执行的代码,它实际上做了某些事情(它创建了一个新的String然后返回它)。可以忽略返回的值,并且仍可以在void-return lambda中使用新的String(“hi”)来创建新的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 return it. 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或Object,但它返回void,因此String无法转换为void error。

#3


21  

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,因此编译器错误。

#4


11  

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类型,接收新String(“hi”)的lambda会将它解释为类似块

{
    new String("hi");
}

which is valid in a void, hence the first case compile.

这在void中是有效的,因此第一种情况是编译。

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中是无效的语法。因此,“hi”唯一要做的就是尝试归还它。

{
    return "hi";
}

which is not valid in a void and explain the error message

在void中无效并解释错误消息

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的类型更改为String,则 - >“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处于错误的调用上下文引起的,所以我将与社区分享这种可能性:

JLS 15.27

JLS 15.27

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 context which is correct.

但是在我们的例子中,我们处于一个正确的调用上下文中。