Promotion de type Java dans les paramètres

20

Je suis tombé sur cet extrait:

public class ParamTest {
    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Cela entraînera une erreur de compilation:

Erreur: (15, 9) java: la référence à printSum est ambiguë à la fois la méthode printSum (int, double) dans ParamTest et la méthode printSum (long, long) dans la correspondance ParamTest

Comment est-ce ambigu? Ne devrait-on pas seulement promouvoir le deuxième paramètre dans ce cas puisque le premier paramètre est déjà un int? Le premier paramètre n'a pas besoin d'être promu dans ce cas, non?

La compilation réussit si je mets à jour le code pour ajouter une autre méthode:

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Permettez-moi de développer juste pour clarifier. Le code ci-dessous crée une ambiguïté:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, long b) {
        System.out.println("In long " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Ensuite, ce code ci-dessous entraîne également une ambiguïté:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(double a, long b) {
        System.out.println("In doubleLONG " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}

Cependant celui-ci n'entraîne pas d'ambiguïté:

public class ParamTest {

    public static void printSum(int a, double b) {
        System.out.println("In intDBL " + (a + b));
    }

    public static void printSum(long a, double b) {
        System.out.println("In longDBL " + (a + b));
    }

    public static void main(String[] args) {
        printSum(1, 2);
    }
}
riruzen
la source
2
Le compilateur peut mapper votre appel printSum (1, 2); à printSum (long a, long b) ou printSum (int a, double b), il est donc ambigu. Vous devez explicitement aider le compilateur à choisir parmi en donnant le type précisément comme ceci: printSum (1, 2d)
Ravindra Ranwala
6
Vous avez mal cité le message d'erreur et la différence est très importante. Le message d'erreur réel est: Error:(15, 9) java: reference to printSum is ambiguous both method printSum(int,double) in ParamTest and method printSum(long,long) in ParamTest match- ce n'est pas la méthode qui est ambiguë, c'est l'appel à la méthode qui est ambiguë.
Erwin Bolwidt
1
@ErwinBolwidt, le message d'erreur provenait d'Eclipse. Désolé, j'ai mal cité. de toute façon, je ne le comprends toujours pas car l'ajout de printSum (int a, long b) supprime le message d'erreur.
riruzen
2
JLS-5.3 => Si le type de l'expression ne peut pas être converti en type de paramètre par une conversion autorisée dans un contexte d'invocation lâche, une erreur de compilation se produit. semble être applicable au contexte, mais pas vraiment simple de déduire comment. +1
Naman
1
Nous avons définitivement besoin d'une question canonique pour cela: stackoverflow.com/… . "
Marco13

Réponses:

17

Je pense que cela a quelque chose à voir avec la règle spécifique de JLS concernant le 15.12.2.5. Choisir la méthode la plus spécifique . Il déclare que:

Si plusieurs méthodes membres sont à la fois accessibles et applicables à un appel de méthode, il est nécessaire d'en choisir une pour fournir le descripteur pour la distribution de la méthode d'exécution. Le langage de programmation Java utilise la règle selon laquelle la méthode la plus spécifique est choisie.

Comment Java choisit la méthode la plus spécifique est expliqué plus en détail dans le texte:

L'intuition informelle est qu'une méthode est plus spécifique qu'une autre si une invocation gérée par la première méthode peut être transmise à l'autre sans erreur de compilation. Dans des cas tels qu'un argument d'expression lambda explicitement typé (§15.27.1) ou une invocation d'arité variable (§15.12.2.4), une certaine flexibilité est permise pour adapter une signature à l'autre.

Dans le cas de votre exemple, toutes les méthodes sont accessibles et applicables à l'appel de méthode, par conséquent, Java doit déterminer laquelle est la plus spécifique .

Pour ces méthodes, aucune ne peut être considérée comme plus spécifique:

public static void printSum(int a, double b) {
    System.out.println("In intDBL " + (a + b));
} // int, double cannot be passed to long, long or double, long without error

public static void printSum(long a, long b) {
    System.out.println("In long " + (a + b));
} // long , long cannot be passed to int, double or double, long without error

public static void printSum(double a, long b) {
    System.out.println("In doubleLONG " + (a + b));
} // double, long cannot be passed to int, double or long, long without error

La quatrième méthode efface l'ambiguïté précisément parce qu'elle remplit la condition nécessaire pour être le plus précis .

public static void printSum(int a, long b) {
    System.out.println(String.format("%s, %s ", a, b));
}

Autrement dit, (int, long) peut être passé à (int, double), (long, long) ou (double, long) sans erreurs de compilation.

émeute
la source
2
Donc, pour résumer, si toutes les méthodes sont accessibles par l'appel, alors java identifie la méthode, qui peut être appelée vers d'autres méthodes sans erreur de temps de compilation, si elle est trouvée, utilisez-la comme la plus spécifique et appelez-la sinon erreur d'ambiguïté? Je pense que celui-ci est une réponse valide @riruzen.
Sandeep Kokate
Merci! J'ai essayé cela avec (double, int) vs (double, long) et cela a vraiment dissipé l'ambiguïté, puis (double, int) vs (long, double) était ambigu comme prévu.
riruzen
7

C'est en effet une question très intéressante. Passons en revue la spécification du langage Java étape par étape.

  1. Lorsque le compilateur essaie d'identifier des méthodes potentiellement applicables, la première chose qu'il fait est de rechercher les méthodes applicables par Strict Invocation .

  2. Dans votre cas, il n'y a pas de telles méthodes, donc la prochaine étape consiste à trouver des méthodes applicables par Loose Invocation

  3. À ce stade, toutes les méthodes correspondent, de sorte que la méthode la plus spécifique ( §15.12.2.5 ) est choisie parmi les méthodes applicables par invocation lâche.

C'est un moment clé, alors regardons cela de près.

Une méthode applicable m1 est plus spécifique qu'une autre méthode applicable m2, pour un appel avec les expressions d'argument e1, ..., ek, si l'une des conditions suivantes est vraie:

(Nous nous intéressons uniquement au cas suivant):

  • m2 n'est pas générique, et m1 et m2 sont applicables par invocation stricte ou lâche, et où m1 a des types de paramètres formels S1, ..., Sn et m2 a des types de paramètres formels T1, ..., Tn, le type Si est plus spécifique que Ti pour l'argument ei pour tout i (1 ≤ i ≤ n, n = k).

Autrement dit, une méthode est plus spécifique si tous ses types de paramètres sont plus spécifiques . Et

Un type S est plus spécifique qu'un type T pour toute expression si S <: T ( §4.10 ).

L'expression S <: Tsignifie que Sc'est un sous-type de T. Pour les primitives, nous avons la relation suivante:

double > float > long > int

Examinons donc vos méthodes et voyons laquelle est plus spécifique que d'autres.

public static void printSum(int a, double b) {  // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(double a, long b) { // method 2
    System.out.println("In doubleLONG " + (a + b));
}

Dans cet exemple, le premier paramètre de la méthode 1 est évidemment plus spécifique que le premier paramètre de la méthode 2 (si vous les appelez avec des valeurs entières:) printSum(1, 2). Mais le deuxième paramètre est plus spécifique pour la méthode 2 , car long < double. Aucune de ces méthodes n'est donc plus spécifique qu'une autre. Voilà pourquoi vous avez une ambiguïté ici.

Dans l'exemple suivant:

public static void printSum(int a, double b) { // method 1
    System.out.println("In intDBL " + (a + b));
}

public static void printSum(long a, double b) { // method 2
    System.out.println("In longDBL " + (a + b));
}

le premier type de paramètre de la méthode 1 est plus spécifique que celui de la méthode 2, car int < longet le deuxième type de paramètre est le même pour les deux, c'est pourquoi la méthode 1 est choisie.

Kirill Simonov
la source
Je n'ai pas diminué votre réponse, mais cette réponse n'explique pas pourquoi (int, double) est ambigu avec (long, long) car clairement (int, double) devrait être plus spécifique en ce qui concerne le premier paramètre.
riruzen
3
@riruzen ça explique: doublen'est pas plus précis que long. Et pour que la méthode soit choisie, tous les paramètres de type doivent être plus spécifiques: le type Si est plus spécifique que Ti pour l'argument ei pour tout i (1 ≤ i ≤ n, n = k)
Kirill Simonov
Elle devrait augmenter +1
Tea
0

car la valeur int peut également être considérée comme double en java. signifie double a = 3est valide et même avec le long long b = 3C'est pourquoi il crée une ambiguïté. Tu appelles

printSum(1, 2);

Est déroutant pour les trois méthodes, car toutes ces trois sont valides:

int a = 1;
double b =1;
long c = 1;

Vous pouvez mettre le L à la fin pour spécifier qu'il s'agit d'une valeur longue. par exemple:

printSum(1L, 2L);

pour le double, vous devez le convertir:

printSum((double)1, 2L);

lire également le commentaire de @Erwin Bolwidt

Sandeep Kokate
la source
Oui compilateur confondu pour les 3 méthodes, le premier compilateur recherche le type exact et s'il n'en trouve pas, essayez de trouver le type compatible, et dans ce cas tous sont compatibles.
Un autre codeur
2
Si j'ai 4 déclarations de méthode au lieu de 3, l'ambiguïté se dégage: public static void printSum (int a, double b) public static void printSum (int a, long b) public static void printSum (long a, long b) public static void printSum (double a, long b) donc même si je suis d'accord avec votre réponse, elle ne répond toujours pas à la question. Java fait une promotion de type automatique si le type exact n'est pas disponible si je ne me trompe pas. Je ne comprends tout simplement pas pourquoi cela devient ambigu avec ces 3.
riruzen