这行绝对是模棱两可的:
doStuff(getPattern(x -> String.valueOf(x)));
从链接的JLS章节重新阅读:
如果满足以下所有条件,则lambda表达式(第15.27节)可能与功能接口类型(第9.8节)兼容:
目标类型的函数类型的奇偶性与lambda表达式的奇偶性相同。
如果目标类型的函数类型具有void返回值,则lambda主体为语句表达式(第14.8节)或与void兼容的块(第15.27.2节)。
如果目标类型的函数类型具有(非无效)返回类型,则lambda主体可以是表达式或值兼容的块(第15.27.2节)。
就您而言,
Consumer您有一个语句表达式,因为即使方法是非无效的,任何方法调用都可以用作语句表达式。例如,您可以简单地编写以下代码:
public void test(Object x) { String.valueOf(x);}它没有意义,但是可以完美编译。您的方法可能会有副作用,编译器对此一无所知。例如,是否
List.add总是返回
true并且没有人关心它的返回值。
当然,该lambda也符合条件,
Function因为它是一种表达。因此这很模棱两可。如果您具有的是表达式而不是 语句表达式
,则调用将被映射为
Function没有任何问题:
doStuff(getPattern(x -> x == null ? "" : String.valueOf(x)));
将其更改为时
{ return String.valueOf(x);},会创建一个值兼容的块,因此它与匹配Function,但不符合
void兼容的块的条件 。但是,您也可能遇到块问题:
doStuff(getPattern(x -> {throw new UnsupportedOperationException();}));此块同时具有值兼容和空值兼容的资格,因此再次有歧义。另一个歧义块示例是一个无限循环:
doStuff(getPattern(x -> {while(true) System.out.println(x);}));至于
System.out.println(x)情况,这有点棘手。它肯定符合 语句表达式的要求
,因此可以与匹配
Consumer,但似乎它与表达式匹配,并且spec表示方法调用是表达式。但是,这是一种有限使用的表达方式,例如15.12.3说:
如果编译时声明为空,则方法调用必须是顶级表达式(即,表达式语句中或for语句的ForInit或ForUpdate部分中的expression),否则会发生编译时错误。这种方法调用不会产生任何值,因此必须仅在不需要值的情况下使用。
因此,编译器完全遵循规范。首先,它确定您的lambda主体符合表达式(即使返回类型为void:15.12.2.1在这种情况下也不例外)和语句表达式的资格,因此也被视为歧义。
因此,对我来说,这两个语句都是根据规范进行编译的。ECJ编译器在此代码上产生相同的错误消息。
通常,我建议您避免重载时具有相同数量的参数并且仅在可接受的功能接口上有所不同的方法重载。即使这些功能接口具有不同的通用性(例如
Consumer和
BiConsumer),您也不会遇到lambda的问题,但可能会遇到方法引用的问题。在这种情况下,只需为您的方法选择其他名称(例如
processStuff和
consumeStuff)。



