Pourquoi le compilateur préfère-t-il une surcharge int à une surcharge de type varargs pour un char?

Code

public class TestOverload { public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");} public static void main(Ssortingng[] args) { new TestOverload('a'); new TestOverload(65); } } 

Sortie

 Int Int 

Est-ce un comportement attendu? Si oui, alors pourquoi? J’attends: char, Int

Note: J’utilise Java 8

Les méthodes avec varargs ( ... ) ont la priorité la plus faible lorsque le compilateur détermine la méthode surchargée à choisir. Par conséquent, TestOverload(int i) est choisi sur TestOverload(char... c) lorsque vous appelez TestOverload avec un seul paramètre de caractère 'a' , car un caractère peut être automatiquement promu en int .

JLS 15.12.2 :

  1. La première phase (§15.12.2.2) effectue une résolution de surcharge sans permettre la conversion par boxing ou unboxing, ni l’utilisation de l’invocation de la méthode d’arity variable . Si aucune méthode applicable n’est trouvée pendant cette phase, le traitement continue jusqu’à la deuxième phase. Cela garantit que tous les appels valides dans le langage de programmation Java avant Java SE 5.0 ne sont pas considérés comme ambigus à la suite de l’introduction des méthodes d’arity variables, de la boxe implicite et / ou du unboxing. Toutefois, la déclaration d’une méthode d’arity variable (§8.1) peut modifier la méthode choisie pour une expression d’invocation de méthode donnée, car une méthode d’arity variable est traitée comme une méthode d’arity fixe dans la première phase. Par exemple, si vous déclarez m (Object …) dans une classe qui déclare déjà m (Object), m (Object) n’est plus choisi pour certaines expressions d’invocation (telles que m (null)), comme m (Object [] ) est plus spécifique.

  2. La deuxième phase (§15.12.2.3) effectue une résolution de surcharge tout en autorisant la boxe et le désencapsulation, mais exclut toujours l’utilisation de l’invocation de méthode d’arity variable . Si aucune méthode applicable n’est trouvée pendant cette phase, le traitement continue jusqu’à la troisième phase. Cela garantit qu’une méthode n’est jamais choisie par le biais d’une invocation de méthode d’arity variable si elle est applicable via une invocation de méthode d’arity fixe.

  3. La troisième phase (§15.12.2.4) permet de combiner la surcharge avec des méthodes d’arité variables , la boxe et le désencapsulation.

MODIFIER:

Si vous souhaitez forcer le compilateur à appeler le constructeur TestOverload(char... c) , vous pouvez transmettre au constructeur un appel de char[] :

 new TestOverload (new char[] {'a'}); 

Oui, c’est un comportement attendu. La priorité pour l’appel de méthode est la suivante:

  1. Le veuf
  2. Boxe
  3. Varargs

Vous trouverez ci-dessous un extrait de documents Java sur le même sujet: –

Le processus de détermination de l’applicabilité commence par la détermination des méthodes potentiellement applicables (§15.12.2.1).

Le rest du processus est divisé en trois phases pour garantir la compatibilité avec les versions du langage de programmation Java antérieures à Java SE 5.0. Les phases sont les suivantes:

La première phase (§15.12.2.2) effectue une résolution de surcharge sans permettre la conversion par boxing ou unboxing, ni l’utilisation de l’invocation de la méthode d’arity variable. Si aucune méthode applicable n’est trouvée pendant cette phase, le traitement continue jusqu’à la deuxième phase.

Cela garantit que tous les appels valides dans le langage de programmation Java avant Java SE 5.0 ne sont pas considérés comme ambigus à la suite de l’introduction des méthodes d’arity variables, de la boxe implicite et / ou du unboxing. Toutefois, la déclaration d’une méthode d’arity variable (§8.1) peut modifier la méthode choisie pour une expression d’invocation de méthode donnée, car une méthode d’arity variable est traitée comme une méthode d’arity fixe dans la première phase. Par exemple, si vous déclarez m (Object …) dans une classe qui déclare déjà m (Object), m (Object) n’est plus choisi pour certaines expressions d’invocation (telles que m (null)), comme m (Object [] ) est plus spécifique.

La deuxième phase (§15.12.2.3) effectue une résolution de surcharge tout en autorisant la boxe et le désencapsulation, mais exclut toujours l’utilisation de l’invocation de méthode d’arity variable. Si aucune méthode applicable n’est trouvée pendant cette phase, le traitement continue jusqu’à la troisième phase.

Cela garantit qu’une méthode n’est jamais choisie par le biais d’une invocation de méthode d’arity variable si elle est applicable via une invocation de méthode d’arity fixe.

La troisième phase (§15.12.2.4) permet de combiner la surcharge avec des méthodes d’arité variables, la boxe et le désencapsulation.

Des conseils solides de Joshua Bloch (Effective Java, 2e éd):

“Ne choisissez comme arguments que pour une méthode surchargée ceux qui ont des types différents de ceux qui sont radicalement différents.”

Un object d’un type radicalement différent est un object qui ne peut pas raisonnablement être converti en un autre des types d’argument. Suivre cette règle peut potentiellement vous faire économiser des heures de débogage d’une erreur mystérieuse qui peut se produire lorsque le compilateur choisit au moment de la compilation la surcharge de méthode à laquelle vous ne vous attendiez pas.

Vos lignes de code violent cette règle et ouvrent la porte à des bogues:

 public TestOverload(int i){System.out.println("Int");} public TestOverload(char... c){System.out.println("char");} 

Un char est inter-convertible avec un int et la seule façon de prévoir ce qui va se passer avec les invocations est d’aller à la spécification de langage Java et de lire les règles quelque peu obscures sur la résolution des surcharges.

Heureusement, cette situation ne devrait pas nécessiter de recherche JLS. Si vous avez des arguments qui ne sont pas radicalement différents les uns des autres, la meilleure option est probablement de ne pas surcharger . Atsortingbuez des noms différents aux méthodes afin d’éviter toute erreur ou confusion de la part de quiconque aurait besoin de maintenir le code.

Le temps, c’est de l’argent.

J’ai pris le code de ce lien et en ai modifié certaines parties:

  public static void main(Ssortingng[] args) { Byte i = 5; byte k = 5; aMethod(i, k); } //method 1 static void aMethod(byte i, Byte k) { System.out.println("Inside 1"); } //method 2 static void aMethod(byte i, int k) { System.out.println("Inside 2"); } //method 3 static void aMethod(Byte i, Byte k) { System.out.println("Inside 3 "); } //method 4 static void aMethod(Byte i, Byte ... k) { System.out.println("Inside 4 "); } 

Le compilateur donne une erreur (La méthode est ambiguë pour le type Surcharge) pour les méthodes 1, 2 et 3 mais pas 4 (pourquoi?)

La réponse réside dans le mécanisme que Java utilise pour faire correspondre les appels de méthodes aux signatures de méthodes. Le mécanisme se déroule en trois phases, dans chaque phase s’il trouve une méthode d’appariement, il s’arrête:

+ première phase: utiliser l’élargissement pour trouver la méthode d’appariement (aucune méthode d’appariement trouvée)

+ phase deux: (aussi) utiliser la boxe / unboxing pour trouver la méthode correspondante (méthode 1,2 et 3 match)

+ phase trois: (aussi) utilise var args (correspond à la méthode 4!)