带有泛型和 varargs 的模棱两可的重载 java 方法

2022-09-01 00:13:05

我试图理解java如何处理函数调用中的歧义。在下面的代码中,对 的调用是不明确的,但不是!!!.methodmethod2

我觉得两者都是模棱两可的,但是当我注释掉对的调用时,为什么会编译呢?为什么没有模棱两可呢?methodmethod2

public class A {
    public static <K> List<K> method(final K arg, final Object... otherArgs) {
        System.out.println("I'm in one");
        return new ArrayList<K>();
    }

    public static <K> List<K> method(final Object... otherArgs) {
        System.out.println("I'm in two");
        return new ArrayList<K>();
    }

    public static <K, V> Map<K, V> method2(final K k0, final V v0, final Object... keysAndValues) {
        System.out.println("I'm in one");
        return new HashMap<K,V> ();
    }

    public static <K, V> Map<K, V> method2(final Object... keysAndValues) {
        System.out.println("I'm in two");
        return new HashMap<K,V>();
    }

    public static void main(String[] args) {
        Map<String, Integer> c = A.method2( "ACD", new Integer(4), "DFAD" );
        //List<Integer> d = A.method(1, "2", 3  );
    }
}

编辑:这在评论中出现:到目前为止,许多IDE报告都是模棱两可的 - IntelliJ和Netbeans。但是,它从命令行/maven编译得很好。


答案 1

一种直观的方法来测试是否比查看是否可以通过使用相同的参数调用来实现更具体method1method2method1method2

method1(params1){
    method2(params1);   // if compiles, method1 is more specific than method2
}

如果有 varargs,我们可能需要扩展一个 vararg,以便 2 个方法具有相同数量的参数。

让我们检查一下示例中的前两个method()

<K> void method_a(K arg, Object... otherArgs) {
    method_b(arg, otherArgs);   //ok L1
}
<K> void method_b(Object arg, Object... otherArgs) { // extract 1 arg from vararg
    method_a(arg, otherArgs);   //ok L2
}

(返回类型不用于确定特异性,因此省略了它们)

两者都编译,因此每个都比另一个更具体,因此模棱两可。你的s也是如此,它们彼此更具体。因此,对的调用是模棱两可的,不应该编译;否则就是编译器错误。method2()method2()


这就是规范所说的;但这合适吗?当然,看起来比 更具体。实际上,如果我们有一个具体的类型而不是method_amethod_bK

void method_a(Integer arg, Object... otherArgs) {
    method_b(arg, otherArgs);   // ok
}
void method_b(Object arg, Object... otherArgs) {
    method_a(arg, otherArgs);   // error
}

则只是比 更具体,反之亦然。method_amethod_b

这种差异源于类型推断的魔力。/ 调用一个没有显式类型参数的泛型方法,因此编译器尝试推断类型参数。类型推断算法的目标是找到类型参数,以便代码编译!难怪L1和L2编译。L2实际上被推断为L1L2this.<Object>method_a(arg, otherArgs)

类型推断试图猜测程序员想要什么,但猜测有时一定是错误的。我们真正的意图其实是

<K> void method_a(K arg, Object... otherArgs) {
    this.<K>method_b(arg, otherArgs);   // ok
}
<K> void method_b(Object arg, Object... otherArgs) {
    this.<K>method_a(arg, otherArgs);   // error
}

答案 2