使用泛型和varargs的不明确的重载java方法

Chi*_*hip 46 java generics variadic-functions

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

我觉得两者都是模棱两可的,但为什么这会在我发出评论时编译method?为什么method2不模棱两可?

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  );
    }
}
Run Code Online (Sandbox Code Playgroud)

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

irr*_*ble 14

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

method1(params1){
    method2(params1);   // if compiles, method1 is more specific than method2
}
Run Code Online (Sandbox Code Playgroud)

如果存在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
}
Run Code Online (Sandbox Code Playgroud)

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

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


这就是规范所说的; 但它是否合适?当然,method_a看起来更具体method_b.实际上,如果我们有一个具体的类型而不是K

void method_a(Integer arg, Object... otherArgs) {
    method_b(arg, otherArgs);   // ok
}
void method_b(Object arg, Object... otherArgs) {
    method_a(arg, otherArgs);   // error
}
Run Code Online (Sandbox Code Playgroud)

然后只是method_a更具体method_b,而不是相反.

这种差异源于类型推理的魔力.L1/ L2调用没有显式类型参数的泛型方法,因此编译器会尝试推断类型参数.类型推断算法的目标是找到类型参数,以便代码编译!难怪L1和L2编译.L2实际上是推断出来的this.<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
}
Run Code Online (Sandbox Code Playgroud)

  • 从分析来看,由于缺乏*最具体的*方法,对`method2`的调用是模棱两可的.因此它是一个编译器错误. (2认同)