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

时间:2015-03-25 17:05:39

标签: java lambda compiler-errors java-8 void

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

4 个答案:

答案 0 :(得分:99)

你的lambda需要与BiConsumer<String, String>一致。如果您参考JLS #15.27.3 (Type of a Lambda)

  

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

     
      
  • [...]
  •   
  • 如果函数类型的结果为void,则lambda主体是语句表达式(第14.8节)或与void兼容的块。
  •   

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

答案 1 :(得分:43)

基本上,new String("hi")是一段可执行的代码,它实际上做了某些事情(它创建一个新的String,然后返回它)。可以忽略返回的值,并且仍然可以在void-return lambda中使用new String("hi")来创建新的String。

但是,"hi"只是一个不会对其自身做任何事情的常量。在lambda体中唯一合理的做法是返回它。但lambda方法必须返回类型StringObject,但它返回void,因此String cannot be casted to void错误。

答案 2 :(得分:21)

第一种情况是好的,因为你正在调用一个特殊的&#34;方法(构造函数),你实际上并没有采取创建的对象。为了更清楚,我将把可选的括号放在你的lambdas中:

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

答案 3 :(得分:11)

JLS指定

  

如果函数类型的结果为void,则lambda主体为a   语句表达式(§14.8)或与void兼容的块。

现在让我们详细了解一下,

由于您的takeBiConsumer方法属于void类型,因此接收new String("hi")的lambda会将其解释为类似

的块
{
    new String("hi");
}

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

但是,在lambda是-> "hi"的情况下,是

这样的块
{
    "hi";
}

在java中不是有效的语法。因此,“hi”唯一要做的就是尝试将其归还。

{
    return "hi";
}

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

incompatible types: bad return type in lambda expression
    java.lang.String cannot be converted to void

为了更好地理解,请注意,如果将takeBiConsumer的类型更改为字符串,-> "hi"将有效,因为它只会尝试直接返回字符串。


请注意,起初我认为错误是由于lambda处于错误的调用环境中引起的,所以我将与社区分享这种可能性:

JLS 15.27

  

如果程序中出现lambda表达式,则是编译时错误   在除了赋值上下文(第5.2节)之外的某个位置,调用   上下文(第5.3节),或者转换上下文(第5.5节)。

但是在我们的情况下,我们处于invocation context这是正确的。