繁体   English   中英

为什么这个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()的调用很好,并且正如预期的那样,我无法将方法引用分配给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 ),但与Exhibit A不同,我无法将方法引用分配给SupplierProcessor 如果它真的很模糊,我觉得我应该能够将s3p3分配给两个重载的参数类型,就像在图表A中一样,但我在p3上得到一个错误,说明error: incompatible types: invalid method reference 图表C中的第二个错误是有意义的, Overloaded::strangelyAmbiguous 不能分配给Processor ,但如果它不可分配,为什么它仍然被认为是不明确的?

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

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

你的问题与这个问题非常相似。

简短的回答是:

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

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

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

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

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

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

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

Processor p = Overloaded::strangelyAmbiguous;

对于这个简单的情况,编译器确实可以推断出正确的方法,我的意思是,人类可以,对于编译器来说应该是没有道理的。 这里的问题是这只是一个简单的案例,只有两种方法,100 * 100的选择呢? 设计者不得不要么允许的东西 (让我们说,到5×5,并允许分辨率像这样)或禁令完全-我猜你应该知道,他们所采取的方法。 这应该是显而易见的,为什么这样的工作,如果你要使用一个拉姆达-元数是真的,明确的。

关于错误信息,这不会是什么新的东西,如果你用lambda表达式和方法引用不够玩,你就会开始错误消息:“非静态方法不能从静态上下文中引用”时,有几乎一无所有这样做。 IIRC这些错误消息已经从java-8及以上改进了,你永远不知道这个错误消息是否会在java-15中得到改善,比方说。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM