2010-03-26 5 views
21

Il semble y avoir un bug dans l'implémentation de Java Varargs. Java ne peut pas distinguer le type approprié lorsqu'une méthode est surchargée avec différents types de paramètres vararg.bug avec varargs et surcharge?

Il me donne une erreur The method ... is ambiguous for the type ...

Consultez le code suivant:

public class Test 
{ 
    public static void main(String[] args) throws Throwable 
    { 
     doit(new int[]{1, 2}); // <- no problem 
     doit(new double[]{1.2, 2.2}); // <- no problem 
     doit(1.2f, 2.2f); // <- no problem 
     doit(1.2d, 2.2d); // <- no problem 
     doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test 
    } 

    public static void doit(double... ds) 
    { 
     System.out.println("doubles"); 
    } 

    public static void doit(int... is) 
    { 
     System.out.println("ints"); 
    } 
} 

le docs disent: « D'une manière générale, vous ne devriez pas surcharger une méthode varargs, ou il sera difficile pour les programmeurs à comprendre quelle surcharge est appelée. "

Cependant, ils ne mentionnent pas cette erreur, et ce ne sont pas les programmeurs qui trouvent cela difficile, c'est le compilateur.

pensées?

EDIT - compilateur: Sun JDK 1.6.0 u18

+13

Peut-être ils parlent des programmeurs qui ont écrit le compilateur. – mob

+1

Quelle version et compilateur? Eclipse ou JDK? –

+0

voir edit - Sun jdk 1.6.0 u18 – pstanton

Répondre

7

Il y a une discussion à ce sujet sur at the Sun Forums.

Aucune vraie résolution là, juste résignation. Varargs (et l'auto-boxing, qui conduit également à un comportement difficile à suivre, en particulier en combinaison avec varargs) ont été verrouillés plus tard dans la vie de Java, et c'est un domaine où cela se voit. Donc c'est plus un bug dans la spécification que dans le compilateur.

Au moins, cela fait de bonnes (?) Questions pièges SCJP.

+5

En fait, il est reconnu comme un bug de compilateur, et le bug a maintenant été corrigé. –

+0

Je suppose que le bug en question est celui que vous pouvez trouver à: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6199075 –

13

Le problème est qu'il est ambigu.

doIt(1, 2); 

pourrait être un appel à doIt(int ...) ou doIt(double ...). Dans ce dernier cas, les littéraux entiers seront promus à double.

Je suis assez sûr que la spécification Java indique qu'il s'agit d'une construction ambiguë, et que le compilateur suit simplement les règles définies par la spécification. (Je dois faire des recherches plus ce pour être sûr.)

EDIT - la partie pertinente de la JLS est « 15.12.2.5 Choosing the Most Specific Method », mais il fait mon mal à la tête.

Je pense que le raisonnement serait que void doIt(int[]) n'est pas plus spécifique (ou vice versa) que void doIt(double[]) parce int[] n'est pas un sous-type de double[] (et vice versa). Puisque les deux surcharges sont également spécifiques, l'appel est ambigu.

En revanche, void doItAgain(int) est plus spécifique que void doItAgain(double) parce int est un sous-type de double selon la JLS. Par conséquent, un appel à doItAgain(42) n'est pas ambigu.

EDIT 2 - @finnw est juste, il est un bug. Considérez cette partie de 15.12.2.5 (modifié pour supprimer les cas non applicables):

Une méthode variable membre arité nommé m est plus spécifique qu'une autre méthode variable membre arité du même nom si:

Une méthode membre a n paramètres et l'autre a k paramètres, où n ≥ k. Les types des paramètres de la première méthode membre sont T1,. . . , Tn-1, Tn [], les types des paramètres de l'autre méthode sont U1,. . . , Uk-1, Uk []. Soit Si = Ui, 1 < = i < = k.Puis:

  • pour tous j de 1 à k-1, Tj <: Sj et,
  • pour tous j de k à n, Tj <: Sk

Appliquer cette au cas où n = k = 1, et nous voyons que doIt(int[]) est plus spécifique que doIt(double[]).


En fait, il y a une bug report pour ce et Sun reconnaît qu'il est en effet un bug, mais ils ont donné la priorité comme « très faible » . Le bogue est maintenant marqué comme corrigé dans Java 7 (b123).

+3

, doIt (int) et doIt (double) devraient aussi être ambigus (sans les varargs). – Thilo

+2

C'est l'interaction avec les varargs qui rend cette ambiguïté, IIRC. Vous avez en fait deux niveaux de promotion, 1) la promotion d'une séquence d'arguments dans un tableau, et 2) la promotion de littéraux intégraux en doubles. –

+2

Je pense que c'est un bogue (ie le comportement du compilateur n'est pas cohérent avec le JLS.) Si je comprends bien cette section du JLS alors 'doIt (int ...)' devrait être strictement plus spécifique que 'doIt (double. ..) 'parce que' int' est un sous-type propre de 'double'. Il est vrai que 'int []' n'est pas un sous-type de 'double []', mais cela ne fait pas partie des exigences, cela ne devrait donc pas affecter la résolution de surcharge. – finnw

4

Intéressant. Heureusement, il existe deux manières différentes pour éviter ce problème:

Vous pouvez utiliser les types d'enveloppe à la place dans les signatures de méthode:

public static void doit(Double... ds) { 
     for(Double currD : ds) { 
      System.out.println(currD); 
     } 
    } 

    public static void doit(Integer... is) { 
     for(Integer currI : is) { 
      System.out.println(currI); 
     } 
    } 

Ou, vous pouvez utiliser les génériques:

public static <T> void doit(T... ts) { 
     for(T currT : ts) { 
     System.out.println(currT); 
     } 
    } 
+2

Vous ne pouvez pas utiliser de génériques, car alors vous avez une seule méthode. Vraisemblablement, le code fait quelque chose d'autre pour les doubles que pour les ints. – Thilo

+0

Bon point. Je vais m'en tenir à l'aide de types de wrapper dans la signature alors: :) –

+2

Hack intéressant. Cela fonctionne parce que l'auto-boxing et la promotion ne sont jamais appliquées au même argument, donc en forçant l'auto-boxing vous empêchez la promotion int-to-double et une seule signature de méthode correspond. Cela ne fonctionnera pas * par exemple # 3 (avec les valeurs 'float'.) – finnw