46

私は、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  );
    }
}

編集:これはコメントで出てきました:多くのIDEはあいまいであると報告しています-これまでのところIntelliJとNetbeansです。ただし、コマンドライン/Mavenからは問題なくコンパイルされます。

4

1 に答える 1

14

同じパラメーターで呼び出して実装できるかどうかを確認するmethod1よりも具体的であるかどうかをテストする直感的な方法method2method1method2

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

可変引数がある場合、2 つのメソッドが同じ数のパラメータを持つように、可変引数を拡張する必要がある場合があります。

method()あなたの例の最初の2つを確認しましょう

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

(戻り値の型は詳細度の決定には使用されないため、省略されます)

両方ともコンパイルされるため、それぞれが他方よりも具体的であるため、あいまいになります。同じことがあなた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
}

then onlymethod_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
}
于 2012-05-18T15:06:03.250 に答える