为什么将此Java方法调用视为模棱两可?

时间:2019-07-19 18:53:52

标签: java overloading method-reference functional-interface arity

我遇到了奇怪的错误消息,我认为这可能是不正确的。考虑以下代码:

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()的调用很好,并且正如预期的那样,我无法将方法引用同时分配给SupplierProcessor,因为它不是模棱两可的: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不同,我无法将方法引用同时分配给SupplierProcessor。如果确实是模棱两可,我觉得我应该能够像表A中一样将s3p3分配给两个重载的参数类型,但是我在p3上遇到错误,指出{ {1}}。附件C中的第二个错误是有道理的,error: incompatible types: invalid method reference 不是可分配给Overloaded::strangelyAmbiguous的,但是如果不能分配,为什么它仍然被认为是模棱两可的?

在确定选择哪个重载版本时,方法引用推断似乎只考虑了FunctionalInterface的优点。在变量分配中,检查了参数的类型,这导致重载方法与变量分配之间存在差异。

在我看来,这就像一个错误。如果不是,那么至少错误消息是不正确的,因为当在两个选择之间只有一个是正确的时,可以说没有歧义。

2 个答案:

答案 0 :(得分:4)

您的问题与this的问题非常相似。

简短的答案是:

Overloaded::genuinelyAmbiguous;
Overloaded::notAmbiguous;
Overloaded::strangelyAmbiguous;

所有这些方法引用都不精确(它们有多个重载)。因此,根据JLS §15.12.2.2.,它们在重载解决期间会从适用性检查中跳过,从而导致模棱两可。

在这种情况下,您需要明确指定类型,例如:

load((Processor) Overloaded::genuinelyAmbiguous);
load(( Supplier) Overloaded::strangelyAmbiguous);

答案 1 :(得分:1)

方法引用和重载,只是……不要。从理论上讲,您是完全正确的-对于编译器来说,这个应该相当容易,但是我们不要混淆人类和编译器。

编译器看到对load的调用,并说:“嘿,我需要调用该方法。很酷,可以吗?其中有2个。当然,让我们匹配参数”。好吧,该参数是对重载方法的方法引用。因此,编译器在这里变得非常困惑,它基本上是这样说的:“如果我可以告诉您所指向的方法引用,那么我可以调用load but ,如果我可以告诉哪个您想调用的load方法,我可以推断出正确的strangelyAmbiguous“,因此它只是绕圈而行,追逐着它的故事。我在编译器“头脑”中做出的决定是我想出的最简单的解释方法。这带来了一个黄金的坏习​​惯-方法重载和方法引用是一个坏主意

但是,您可能会说-ARITY!参数的数量是编译器(可能)在确定是否为重载时要做的第一件事,这正是您的意思:

Processor p = Overloaded::strangelyAmbiguous;

在这种简单情况下,编译器确实可以推断出正确的方法,我的意思是,我们人类应该对编译器不费吹灰之力。这里的问题是,这是一个只有两种方法的简单情况,那么100 * 100的选择呢?设计师必须要么允许某事(允许说5 * 5,并允许这样的分辨率)或完全禁止这样做-我想您知道他们采取的方法。很明显,如果您要使用lambda,为什么这样做会起作用-明确表示Arity是 right

关于错误消息,这并不是什么新鲜的东西,如果您对lambda和方法引用玩够了,您将开始讨厌错误消息:“无法从以下位置引用非静态方法静态上下文”,则与字面意义无关。 IIRC这些错误消息从java-8起已经得到改善,您永远不知道该错误消息是否也会在java-15中得到改善。