为什么这个 Java 方法调用被认为是不明确的?
Why is this Java method call considered ambiguous?
我遇到了一条奇怪的错误消息,我认为它可能不正确。考虑以下代码:
public class Overloaded {
public interface Supplier {
int get();
}
public interface Processor {
String process(String s);
}
public static void load(Supplier s) {}
public static void load(Processor p) {}
public static int genuinelyAmbiguous() { return 4; }
public static String genuinelyAmbiguous(String s) { return "string"; }
public static int notAmbiguous() { return 4; }
public static String notAmbiguous(int x, int y) { return "string"; }
public static int strangelyAmbiguous() { return 4; }
public static String strangelyAmbiguous(int x) { return "string"; }
}
如果我有这样的方法:
// Exhibit A
public static void exhibitA() {
// Genuinely ambiguous: either choice is correct
load(Overloaded::genuinelyAmbiguous); // <-- ERROR
Supplier s1 = Overloaded::genuinelyAmbiguous;
Processor p1 = Overloaded::genuinelyAmbiguous;
}
我们得到的错误很有道理; load()
的参数可以分配给任何一个,所以我们得到一个错误,指出方法调用不明确。
相反,如果我有一个如下所示的方法:
// Exhibit B
public static void exhibitB() {
// Correctly infers the right overloaded method
load(Overloaded::notAmbiguous);
Supplier s2 = Overloaded::notAmbiguous;
Processor p2 = Overloaded::notAmbiguous; // <-- ERROR
}
对 load()
的调用没问题,正如预期的那样,我无法将方法引用分配给 Supplier
和 Processor
,因为它没有歧义:Overloaded::notAmbiguous
无法分配给 p2
.
现在是奇怪的。如果我有这样的方法:
// Exhibit C
public static void exhibitC() {
// Complains that the reference is ambiguous
load(Overloaded::strangelyAmbiguous); // <-- ERROR
Supplier s3 = Overloaded::strangelyAmbiguous;
Processor p3 = Overloaded::strangelyAmbiguous; // <-- ERROR
}
编译器抱怨对 load()
的调用不明确 (error: reference to load is ambiguous
),但与图表 A 不同的是,我无法将方法引用同时分配给 Supplier
和 Processor
.如果它真的不明确,我觉得我应该能够将 s3
和 p3
分配给两个重载参数类型,就像在图表 A 中一样,但是我在 p3
上得到一个错误,指出 error: incompatible types: invalid method reference
。图表 C 中的第二个错误是有道理的,Overloaded::strangelyAmbiguous
不是 可分配给 Processor
,但如果它不可分配,为什么它仍然被认为是模棱两可的?
似乎方法引用推理在确定哪个重载版本到 select 时只查看 FunctionalInterface 的参数。在变量赋值中,检查了arity 和类型的参数,这导致了重载方法和变量赋值之间的差异。
在我看来这像是一个错误。如果不是,至少错误消息是不正确的,因为在两个选择之间只有一个是正确的时可以说没有歧义。
你的问题与一个非常相似。
简短的回答是:
Overloaded::genuinelyAmbiguous;
Overloaded::notAmbiguous;
Overloaded::strangelyAmbiguous;
所有这些方法引用都不准确(它们有多个重载)。因此,根据 JLS §15.12.2.2.,它们在重载解析期间从适用性检查中跳过,这会导致歧义。
此时需要明确指定类型,例如:
load((Processor) Overloaded::genuinelyAmbiguous);
load(( Supplier) Overloaded::strangelyAmbiguous);
方法引用和重载,只是...不要。从理论上讲,你是正确的 - 这 应该 对于编译器来说相当容易推断,但我们不要混淆人类和编译器。
编译器看到对 load
的调用并说:"hey, I need to call that method. Cool, can I? Well there are 2 of them. Sure, let's match the argument"。好吧,参数是对重载方法的方法引用。所以编译器在这里真的很困惑,它基本上说:"if I could tell which method reference you are pointing to, I could call load
, but, if I could tell which load
method you want to call, I could infer the correct strangelyAmbiguous
",因此它只是绕圈子,追逐它的故事。这个由编译器 "mind" 做出的决定是我能想到的最简单的解释方式。这带来了一个黄金坏习惯 - 方法重载和方法引用是一个坏主意。
但是,您可能会说 - ARITY!参数的数量是编译器在决定这是否是重载时(可能)做的第一件事,正是你的观点:
Processor p = Overloaded::strangelyAmbiguous;
对于这种简单的情况,编译器确实可以推断出正确的方法,我的意思是 我们人类 可以,对于编译器来说应该是轻而易举的事情。这里的问题是,这是一个简单的案例,只有 2 个方法,那么 100*100 个选择呢?设计师必须要么允许某事(比如最多 5*5 并允许这样的分辨率)或完全禁止它 - 我猜你知道他们走过的路。如果您使用 lambda,为什么这会起作用应该是显而易见的 - arity 是 right 那里,明确的。
关于错误消息,这不是什么新鲜事,如果您对 lambda 和方法引用玩够了,您将开始讨厌错误消息:"a non static method cannot be referenced from a static context",当字面上与此无关时。 IIRC 这些错误消息在 java-8 及更高版本中有所改进,你永远不知道此错误消息是否也会在 java-15 中得到改进,比方说。
我遇到了一条奇怪的错误消息,我认为它可能不正确。考虑以下代码:
public class Overloaded {
public interface Supplier {
int get();
}
public interface Processor {
String process(String s);
}
public static void load(Supplier s) {}
public static void load(Processor p) {}
public static int genuinelyAmbiguous() { return 4; }
public static String genuinelyAmbiguous(String s) { return "string"; }
public static int notAmbiguous() { return 4; }
public static String notAmbiguous(int x, int y) { return "string"; }
public static int strangelyAmbiguous() { return 4; }
public static String strangelyAmbiguous(int x) { return "string"; }
}
如果我有这样的方法:
// Exhibit A
public static void exhibitA() {
// Genuinely ambiguous: either choice is correct
load(Overloaded::genuinelyAmbiguous); // <-- ERROR
Supplier s1 = Overloaded::genuinelyAmbiguous;
Processor p1 = Overloaded::genuinelyAmbiguous;
}
我们得到的错误很有道理; load()
的参数可以分配给任何一个,所以我们得到一个错误,指出方法调用不明确。
相反,如果我有一个如下所示的方法:
// Exhibit B
public static void exhibitB() {
// Correctly infers the right overloaded method
load(Overloaded::notAmbiguous);
Supplier s2 = Overloaded::notAmbiguous;
Processor p2 = Overloaded::notAmbiguous; // <-- ERROR
}
对 load()
的调用没问题,正如预期的那样,我无法将方法引用分配给 Supplier
和 Processor
,因为它没有歧义:Overloaded::notAmbiguous
无法分配给 p2
.
现在是奇怪的。如果我有这样的方法:
// Exhibit C
public static void exhibitC() {
// Complains that the reference is ambiguous
load(Overloaded::strangelyAmbiguous); // <-- ERROR
Supplier s3 = Overloaded::strangelyAmbiguous;
Processor p3 = Overloaded::strangelyAmbiguous; // <-- ERROR
}
编译器抱怨对 load()
的调用不明确 (error: reference to load is ambiguous
),但与图表 A 不同的是,我无法将方法引用同时分配给 Supplier
和 Processor
.如果它真的不明确,我觉得我应该能够将 s3
和 p3
分配给两个重载参数类型,就像在图表 A 中一样,但是我在 p3
上得到一个错误,指出 error: incompatible types: invalid method reference
。图表 C 中的第二个错误是有道理的,Overloaded::strangelyAmbiguous
不是 可分配给 Processor
,但如果它不可分配,为什么它仍然被认为是模棱两可的?
似乎方法引用推理在确定哪个重载版本到 select 时只查看 FunctionalInterface 的参数。在变量赋值中,检查了arity 和类型的参数,这导致了重载方法和变量赋值之间的差异。
在我看来这像是一个错误。如果不是,至少错误消息是不正确的,因为在两个选择之间只有一个是正确的时可以说没有歧义。
你的问题与
简短的回答是:
Overloaded::genuinelyAmbiguous;
Overloaded::notAmbiguous;
Overloaded::strangelyAmbiguous;
所有这些方法引用都不准确(它们有多个重载)。因此,根据 JLS §15.12.2.2.,它们在重载解析期间从适用性检查中跳过,这会导致歧义。
此时需要明确指定类型,例如:
load((Processor) Overloaded::genuinelyAmbiguous);
load(( Supplier) Overloaded::strangelyAmbiguous);
方法引用和重载,只是...不要。从理论上讲,你是正确的 - 这 应该 对于编译器来说相当容易推断,但我们不要混淆人类和编译器。
编译器看到对 load
的调用并说:"hey, I need to call that method. Cool, can I? Well there are 2 of them. Sure, let's match the argument"。好吧,参数是对重载方法的方法引用。所以编译器在这里真的很困惑,它基本上说:"if I could tell which method reference you are pointing to, I could call load
, but, if I could tell which load
method you want to call, I could infer the correct strangelyAmbiguous
",因此它只是绕圈子,追逐它的故事。这个由编译器 "mind" 做出的决定是我能想到的最简单的解释方式。这带来了一个黄金坏习惯 - 方法重载和方法引用是一个坏主意。
但是,您可能会说 - ARITY!参数的数量是编译器在决定这是否是重载时(可能)做的第一件事,正是你的观点:
Processor p = Overloaded::strangelyAmbiguous;
对于这种简单的情况,编译器确实可以推断出正确的方法,我的意思是 我们人类 可以,对于编译器来说应该是轻而易举的事情。这里的问题是,这是一个简单的案例,只有 2 个方法,那么 100*100 个选择呢?设计师必须要么允许某事(比如最多 5*5 并允许这样的分辨率)或完全禁止它 - 我猜你知道他们走过的路。如果您使用 lambda,为什么这会起作用应该是显而易见的 - arity 是 right 那里,明确的。
关于错误消息,这不是什么新鲜事,如果您对 lambda 和方法引用玩够了,您将开始讨厌错误消息:"a non static method cannot be referenced from a static context",当字面上与此无关时。 IIRC 这些错误消息在 java-8 及更高版本中有所改进,你永远不知道此错误消息是否也会在 java-15 中得到改进,比方说。