Question

J'ai remarqué aujourd'hui que la boxe automatique peut parfois causer une ambiguïté dans la résolution de la surcharge de la méthode. L’exemple le plus simple semble être ceci:

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

Une fois compilé, cela 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 solution à cette erreur est triviale: utilisez simplement la boxe automatique explicite:

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

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

Alors, pourquoi la résolution de la surcharge a-t-elle échoué? Pourquoi le compilateur n'a-t-il pas automatiquement coché le premier argument et accepté normalement le second argument? Pourquoi ai-je dû demander explicitement la boxe automatique?

Était-ce utile?

La solution

Lorsque vous convertissez vous-même le premier argument en objet, le compilateur correspond à la méthode sans utiliser l'auto-sélection (JLS3 15.12.2):

  

La première phase (& # 167; 15.12.2.2) exécute   résolution de surcharge sans autorisation   conversion de boxe ou de déballage, ou   utilisation de la méthode d'arity variable   invocation. Si aucune méthode applicable n'est   trouvé pendant cette phase puis   le traitement se poursuit à la seconde   phase.

Si vous ne le lancez pas explicitement, la deuxième étape de la recherche d'une méthode de correspondance, autorisant la méthode de la sélection automatique, est ensuite ambiguë, car votre deuxième argument peut être comparé à un booléen ou à un objet.

  

La deuxième phase (& # 167; 15.12.2.3) exécute   résolution de surcharge tout en permettant   boxe et unboxing, mais toujours   exclut l'utilisation d'arité variable   invocation de méthode.

Pourquoi, dans la seconde phase, le compilateur ne choisit-il pas la seconde méthode car aucun argument automatique de l'argument booléen n'est nécessaire? Parce qu'après avoir 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, indépendamment de la boxe ou de la déconnexion qui a eu lieu pour les faire correspondre (& # 167; 15.12. 2.5).

Aussi: le compilateur ne peut pas toujours choisir la méthode la plus spécifique en fonction du nombre de box (auto) boxing nécessaires. Il peut toujours en résulter des cas ambigus. Par exemple, cela reste 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
}

N'oubliez pas que l'algorithme permettant de choisir une méthode correspondante (étape 2 de la compilation) est fixé et décrit dans le JLS. Une fois en phase 2, il n'y a plus de sélection automatique ni de déconditionnement. Le compilateur localisera toutes les méthodes accessibles (les deux méthodes dans ces cas) et applicables (à nouveau les deux méthodes), et choisira ensuite la méthode la plus spécifique sans regarder boxing / unboxing, qui est ambigu ici.

Autres conseils

Le compilateur a coché automatiquement le premier argument. Une fois cela fait, c'est le deuxième argument qui est ambigu, car il pourrait être perçu comme un booléen ou un objet.

Cette page explique les règles de sélection automatique et de méthode. invoquer. Le compilateur essaie d’abord de sélectionner une méthode sans utiliser la moindre substitution automatique , car les opérations de boxe et de déballage entraînent des pénalités de performances. Si aucune méthode ne peut être sélectionnée sans recourir à la boxe, comme dans ce cas, la boxe est sur la table pour tous les arguments de cette méthode.

Lorsque vous dites f (a, b ), le compilateur ne sait pas à quelle fonction il doit faire référence.

Cela est dû au fait que un est un int , mais que l'argument attendu dans f est un objet. Le responsable de la publication décide donc de convertir a en objet. Le problème est que si un peut être converti en un objet, il peut en être de même 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 manuellement a en objet, le compilateur recherche uniquement la correspondance la plus proche, puis y fait référence.

  

Pourquoi le compilateur n'a-t-il pas sélectionné le   fonction accessible en & "faire"   le moins possible de   Conversions de boxe / déballage & ";?

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 sélectionner? C'est ambigu non? De même, cela deviendra plus complexe lorsque de nombreux arguments seront présents. Le compilateur a donc choisi de vous avertir à la place.

Puisqu'il n'y a aucun moyen de savoir laquelle des fonctions le programmeur avait vraiment l'intention d'appeler, le compilateur donne une erreur.

  

Alors pourquoi la résolution de surcharge   échouer? Pourquoi la boîte automatique du compilateur   le premier argument, et accepter le   deuxième argument normalement? Pourquoi ai-je   avoir à demander la boxe automatique   explicitement?

Il n'a pas accepté le deuxième argument normalement. Rappelez-vous que & "; Boolean &"; peut être mis en boîte à un objet aussi. Vous auriez pu aussi explicitement transtyper l'argument booléen dans Object et cela aurait fonctionné.

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

Le casting est utile car aucune boxe n'est nécessaire pour trouver la méthode à appeler. Sans le casting, le deuxième essai consiste à autoriser la boxe, puis le booléen peut être mis en boîte.

Il vaut mieux avoir des spécifications claires et compréhensibles pour dire ce qui va se passer que de laisser les gens deviner.

Le compilateur Java résout les méthodes et les constructeurs surchargés en plusieurs phases. Dans la première phase [& # 167; 15.12.2.2], il identifie les méthodes applicables en sous-typant [& # 167; 4.10]. Dans cet exemple, aucune des méthodes n'est applicable, car int n'est pas un sous-type d'Object.

Au cours de la deuxième phase [& # 167; 15.12.2.3], le compilateur identifie les méthodes applicables par la conversion d’appel de méthode [& # 167; 5.3], qui associe la sélection automatique et le sous-typage. L'argument int peut être converti en Integer, qui est un sous-type de Object, pour les deux surcharges. L'argument booléen ne nécessite aucune conversion pour la première surcharge et peut être converti en booléen, un sous-type d'objet, pour la seconde. Par conséquent, les deux méthodes sont applicables dans la deuxième phase.

Etant donné que plusieurs méthodes sont applicables, le compilateur doit déterminer celle qui est la plus spécifique [& # 167; 15.12.2.5]. Il compare les types de paramètres, pas les types d'arguments, et ne les remplace pas automatiquement. Object et boolean sont des types non liés, ils sont donc considérés comme tout aussi spécifiques. Aucune méthode n'est plus spécifique que l'autre, l'appel est donc ambigu.

Une façon de résoudre cette ambiguïté serait de changer le paramètre booléen en un type booléen, qui est un sous-type d’objet. La première surcharge serait toujours plus spécifique (le cas échéant) que la seconde.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top