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; 
}

我们得到的错误是完全合理的;参数 toload()可以分配给任何一个,所以我们得到一个错误,指出方法调用不明确。

相反,如果我有一个看起来像这样的方法:

// 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。如果它真的模棱两可,我觉得我应该能够像在 Exhibit A 中那样为这两种重载参数类型分配s3和分配,但是我在声明. Exhibit C 中的第二个错误是有道理的,不能分配给,但是如果它不能分配,为什么它仍然被认为是模棱两可的?p3p3error: incompatible types: invalid method referenceOverloaded::strangelyAmbiguous Processor

在确定选择哪个重载版本时,方法引用推断似乎只查看 FunctionalInterface 的数量。在变量赋值中,检查参数的数量类型,这导致重载方法和变量赋值之间存在这种差异。

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

4

2 回答 2

6

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

简短的回答是:

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

所有这些方法引用都是不准确的(它们有多个重载)。因此,根据JLS §15.12.2.2。,它们在重载解决期间从适用性检查中被跳过,这会导致歧义。

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

load((Processor) Overloaded::genuinelyAmbiguous);
load(( Supplier) Overloaded::strangelyAmbiguous);
于 2019-07-19T20:07:44.423 回答
3

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

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

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

Processor p = Overloaded::strangelyAmbiguous;

对于这个简单的案例,编译器确实可以推断出正确的方法,我的意思是,我们人类可以,对编译器来说应该是不费吹灰之力的。这里的问题是,这是一个只有 2 个方法的简单案例,那么 100*100 个选项呢?设计师必须要么允许某些东西(比如说最多 5*5 并允许像这样的分辨率),要么完全禁止它——我想你知道他们所走的路。如果您使用 lambda,这应该很明显为什么会起作用 - arity 就那里,明确的。

关于错误消息,这不是什么新鲜事,如果您对 lambda 和方法引用玩得足够多,您将开始讨厌错误消息:“无法从静态上下文引用非静态方法”,实际上什么都没有与此有关。IIRC 这些错误消息已从 java-8 及更高版本改进,你永远不知道此错误消息是否也会在 java-15 中得到改进,比方说。

于 2019-07-20T13:10:49.580 回答