46 votes

Méthodes java surchargées ambiguës avec génériques et varargues

J'essaie de comprendre comment Java traite les ambiguïtés dans les appels de fonction. Dans le code suivant, l'appel à method est ambiguë, mais method2 n'est pas ! !!.

Je pense que les deux sont ambigus, mais pourquoi cela compile-t-il lorsque je commente l'appel à method ? Pourquoi le method2 n'est pas non plus ambiguë ?

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

EDIT : Ceci est apparu dans les commentaires : Un certain nombre d'IDEs signalent les deux comme ambigus - IntelliJ et Netbeans jusqu'à présent. Cependant, il compile très bien en ligne de commande/maven.

14voto

irreputable Points 25577

Une manière intuitive de tester si method1 est plus spécifique que metho2 est de voir si method1 peut être mis en œuvre en invoquant method2 avec les mêmes paramètres

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

S'il y a des varargs, nous pouvons avoir besoin de développer un vararg pour que deux méthodes aient le même nombre de paramètres.

Vérifions les deux premiers method() dans votre exemple

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

(les types de retour ne sont pas utilisés pour déterminer la spécificité, ils sont donc omis)

Les deux compilent, donc chacun est plus spécifique que l'autre, d'où l'ambiguïté. Il en va de même pour votre method2() ils sont plus spécifiques les uns que les autres. Par conséquent, l'appel à method2() est ambiguë et ne devrait pas compiler ; sinon c'est un bug de compilation.


C'est donc ce que dit la spécification, mais est-ce correct ? Certainement, method_a semble plus spécifique que method_b . En fait, si nous avons un type concret au lieu de 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
}

alors seulement method_a est plus spécifique que method_b et non l'inverse.

La divergence provient de la magie de l'inférence de type. L1 / L2 appelle une méthode générique sans arguments de type explicites, le compilateur essaie donc de déduire les arguments de type. Le site objectif de l'algorithme d'inférence de type est de trouver des arguments de type de sorte que le code compile ! Pas étonnant que L1 et L2 compilent. L2 est en fait déduit être this.<Object>method_a(arg, otherArgs)

L'inférence de type tente de deviner ce que le programmeur veut, mais cette supposition doit parfois être erronée. Notre intention réelle est en fait

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X