【发布时间】:2015-08-15 14:02:12
【问题描述】:
我有一个重载方法,它分别接受消费者和函数对象,并返回与相应消费者/函数匹配的泛型类型。我认为这很好,但是当我尝试使用 lambda 表达式调用任一方法时,我收到一个错误,表明对该方法的引用不明确。
根据我对JLS §15.12.2.1. Identify Potentially Applicable Methods: 的阅读,编译器似乎应该知道我的带有 void 块的 lambda 与 Consumer 方法匹配,而我的带有返回类型的 lambda 与 Function 方法匹配。
我整理了以下无法编译的示例代码:
import java.util.function.Consumer;
import java.util.function.Function;
public class AmbiguityBug {
public static void main(String[] args) {
doStuff(getPattern(x -> System.out.println(x)));
doStuff(getPattern(x -> String.valueOf(x)));
}
static Pattern<String, String> getPattern(Function<String, String> function) {
return new Pattern<>(function);
}
static ConsumablePattern<String> getPattern(Consumer<String> consumer) {
return new ConsumablePattern<>(consumer);
}
static void doStuff(Pattern<String, String> pattern) {
String result = pattern.apply("Hello World");
System.out.println(result);
}
static void doStuff(ConsumablePattern<String> consumablePattern) {
consumablePattern.consume("Hello World");
}
public static class Pattern<T, R> {
private final Function<T, R> function;
public Pattern(Function<T, R> function) {
this.function = function;
}
public R apply(T value) {
return function.apply(value);
}
}
public static class ConsumablePattern<T> {
private final Consumer<T> consumer;
public ConsumablePattern(Consumer<T> consumer) {
this.consumer = consumer;
}
public void consume(T value) {
consumer.accept(value);
}
}
}
我还发现了一个 similar stackoverflow 帖子,结果证明这是一个编译器错误。我的情况非常相似,虽然有点复杂。对我来说,这仍然看起来像一个错误,但我想确保我没有误解 lambdas 的语言规范。我正在使用 Java 8u45,它应该具有所有最新修复。
如果我将我的方法调用更改为包含在一个块中,一切似乎都可以编译,但这会增加额外的冗长,并且许多自动格式化程序会将其重新格式化为多行。
doStuff(getPattern(x -> { System.out.println(x); }));
doStuff(getPattern(x -> { return String.valueOf(x); }));
【问题讨论】:
-
(应该)使用消费者的 getPattern 调用都缺少右括号(由于最小字符限制,我无法编辑它)。
-
另一种解决方法是指定 x 的类型:
doStuff(getPattern((String x) -> System.out.println(x)));doStuff(getPattern((String x) -> String.valueOf(x))); -
@Misha:这对我来说实际上很奇怪。我没有合理的解释为什么添加
x类型会改变事情。您是否期望此案例将映射到Function:Map<String, String> map = new HashMap<>();getPattern((String x) -> map.remove(x));? -
@TagirValeev 见docs.oracle.com/javase/specs/jls/se8/html/…。如果 lambda 是显式类型的,则有一些特殊规则可以确定功能接口类型之间的“更具体”关系。根据这些规则,
Function似乎比Consumer更具体 -
您可以使用
x -> (String.valueOf(x))调用基于Function的方法,使用x->{String.valueOf(x);}调用基于Consumer的方法。但是考虑到这些方法做完全不同的事情并产生不同类型的结果,这会导致另一个方法的重载选择再次做完全不同的事情,为这些方法使用相同的名称是一种可怕的伪装。
标签: java lambda java-8 overloading jls