34 votes

Pourquoi l'autoboxing rend-il certains appels ambigus en Java ?

J'ai remarqué aujourd'hui que l'auto-boxing peut parfois provoquer une ambiguïté dans la résolution des surcharges de méthodes. L'exemple le plus simple est le suivant :

public class Test {
    static void f(Object a, boolean b) {}
    static void f(Object a, Object b) {}

    static void m(int a, boolean b) { f(a,b); }
}

Lorsqu'il est compilé, il provoque l'erreur suivante :

Test.java:5: reference to f is ambiguous, both method
    f(java.lang.Object,boolean) in Test and method
    f(java.lang.Object,java.lang.Object) in Test match

static void m(int a, boolean b) { f(a, b); }
                                  ^

La correction de cette erreur est triviale : il suffit d'utiliser un auto-boxing explicite :

static void m(int a, boolean b) { f((Object)a, b); }

Ce qui appelle correctement la première surcharge comme prévu.

Alors pourquoi la résolution de surcharge a-t-elle échoué ? Pourquoi le compilateur n'a-t-il pas mis le premier argument en boîte automatique et accepté le second argument normalement ? Pourquoi ai-je dû demander l'auto-boxing explicitement ?

32voto

eljenso Points 7690

Lorsque vous transformez vous-même le premier argument en objet, le compilateur fait correspondre la méthode sans utiliser l'autoboxing (JLS3 15.12.2) :

La première phase (§15.12.2.2) effectue la résolution de surcharge sans permettre la conversion par boitage ou débitage, ou l'utilisation de l'utilisation de méthodes d'arité variable invocation de méthodes d'arité variable. Si aucune méthode applicable n'est est trouvée pendant cette phase, le le traitement se poursuit au deuxième phase.

Si vous ne le castez pas explicitement, il passera à la deuxième phase en essayant de trouver une méthode correspondante, permettant l'autoboxage, et alors il est effectivement ambigu, parce que votre deuxième argument peut être assorti d'un booléen ou d'un objet.

La deuxième phase (§15.12.2.3) réalise la résolution de surcharge tout en permettant l'encapsulation et la désencapsulation, mais exclut toujours l'utilisation de l'arité variable pour l'invocation de méthodes.

Pourquoi, dans la deuxième phase, le compilateur ne choisit-il pas la deuxième méthode parce qu'il n'est pas nécessaire d'effectuer un autocontrôle de l'argument booléen ? Parce qu'une fois qu'il a trouvé les deux méthodes correspondantes, seule la conversion de sous-type est utilisée pour déterminer la méthode la plus spécifique des deux, indépendamment de tout boxing ou unboxing qui a eu lieu pour les faire correspondre en premier lieu (§15.12.2.5).

Aussi : le compilateur ne peut pas toujours choisir la méthode la plus spécifique basée sur le nombre d'auto(un)boxing nécessaires. Cela peut encore donner lieu à des cas ambigus. Par exemple, ceci est toujours ambigu :

public class Test {
    static void f(Object a, boolean b) {}
    static void f(int a, Object b) {}

    static void m(int a, boolean b) { f(a, b); } // ambiguous
}

Rappelez-vous que l'algorithme pour choisir une méthode d'appariement (étape 2 de la compilation) est fixé et décrit dans le JLS. Une fois dans la phase 2, il n'y a pas d'autoboxing ou de unboxing sélectif. Le compilateur va localiser todo les méthodes qui sont accessibles (les deux méthodes dans ces cas) et applicables (à nouveau les deux méthodes), et ensuite seulement choisit la plus spécifique sans regarder le boxing/unboxing, qui est ambigu ici.

4voto

Bill the Lizard Points 147311

Le compilateur a fait auto-boxe le premier argument. Une fois que cela a été fait, c'est le deuxième argument qui est ambigu, car il pourrait être vu comme un booléen ou un objet.

Cette page explique les règles relatives à la boîte automatique et à la sélection de la méthode à invoquer. Le compilateur essaie d'abord de sélectionner une méthode sans utiliser la moindre boîte automatique car le boxing et le unboxing entraînent des pénalités de performance. Si aucune méthode ne peut être sélectionnée sans recourir à la mise en boîte, comme c'est le cas ici, alors la mise en boîte est sur la table pour la mise en boîte. todo à cette méthode.

3voto

Niyaz Points 16307

Quand vous dites f(a, b ), le compilateur est confus quant à la fonction à laquelle il doit faire référence.

Cela s'explique par le fait que a est un int mais l'argument attendu dans f est un objet. Le compilateur décide donc de convertir a à un objet. Maintenant, le problème est que, si a peut être converti en objet, donc peut être b .

Cela signifie que l'appel de fonction peut faire référence à l'une ou l'autre des définitions. Cela rend l'appel ambigu.

Lorsque vous convertissez a à un objet manuellement, le compilateur recherche simplement la correspondance la plus proche et y fait référence.

Pourquoi le compilateur n'a pas sélectionné le fonction qui peut être atteinte en "faisant le plus petit nombre possible de de conversions boîte/déboîte" ?

Voir le cas suivant :

f(boolean a, Object b)
f(Object a , boolean b)

Si nous appelons comme f(booléen a, booléen b) quelle fonction doit-il choisir ? C'est ambigu, non ? De même, cela devient plus complexe lorsque beaucoup d'arguments sont présents. Le compilateur a donc choisi de vous donner un avertissement à la place.

Comme il n'y a aucun moyen de savoir laquelle des fonctions le programmeur avait réellement l'intention d'appeler, le compilateur émet une erreur.

2voto

Kevin Points 19613

Alors pourquoi la résolution de la surcharge a-t-elle échoué ? Pourquoi le compilateur n'a pas auto-boxé le premier argument, et accepte le deuxième argument normalement ? Pourquoi ai-je devais-je demander l'auto-boxing explicitement ?

Il n'a pas accepté le second argument normalement. Rappelez-vous que "booléen" peut aussi être mis en boîte à un Objet. Vous auriez pu explicitement transformer l'argument booléen en objet et cela aurait fonctionné.

2voto

iny Points 3925

Voir http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

Le cast est utile car il n'y a pas besoin de boxer pour trouver la méthode à appeler. Sans le cast, la deuxième tentative est d'autoriser la mise en boîte et alors le booléen peut également être mis en boîte.

Il est préférable d'avoir des spécifications claires et compréhensibles pour dire ce qui va se passer plutôt que de laisser les gens deviner.

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