2010-03-26 18 views
21

Es scheint einen Fehler in der Java varargs-Implementierung zu geben. Java kann den geeigneten Typ nicht unterscheiden, wenn eine Methode mit verschiedenen vararg-Parametern überladen ist.Fehler mit Varargs und Überladung?

Es gibt mir einen Fehler The method ... is ambiguous for the type ...

den folgenden Code vor:

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

die docs sagen: „Generell sollten Sie keine varargs Methode überlasten, oder es wird für Programmierer schwierig sein, herauszufinden, welche Überladung aufgerufen wird. "

aber sie erwähnen diesen Fehler nicht, und es ist nicht die Programmierer, die es schwierig finden, es ist der Compiler.

Gedanken?

EDIT - Compiler: Sun JDK 1.6.0 u18

+13

Vielleicht Sie sprechen über die Programmierer, die den Compiler geschrieben haben. – mob

+1

Welche Version und Compiler? Eclipse oder JDK? –

+0

siehe bearbeiten - Sun jdk 1.6.0 u18 – pstanton

Antwort

7

Es gibt eine Diskussion darüber über at the Sun Forums.

Keine echte Auflösung dort, nur Resignation.

Varargs (und Auto-Boxen, die auch zu schwer zu folgen Verhalten, vor allem in Kombination mit Varargs führt) wurden später in Java Leben, und das ist ein Bereich, wo es zeigt. Es ist also eher ein Fehler in der Spezifikation als im Compiler.

Zumindest macht es gute (?) SCJP-Trickfragen.

+5

Eigentlich ist es als Compiler-Fehler anerkannt, und der Fehler wurde jetzt behoben. –

+0

Ich vermute, der Fehler in Frage ist derjenige, den Sie finden können: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6199075 –

13

Das Problem ist, dass es nicht eindeutig ist.

doIt(1, 2); 

könnte ein Aufruf an doIt(int ...) oder doIt(double ...) sein. Im letzteren Fall werden die Ganzzahlliterale zu double Werten hochgestuft.

Ich bin ziemlich sicher, dass die Java-Spezifikation sagt, dass dies ein mehrdeutiges Konstrukt ist, und der Compiler nur die Regeln der Spezifikation folgt. (Ich hätte diese weiter zu erforschen sicher zu sein.)

EDIT - der relevante Teil des JLS ist „15.12.2.5 Choosing the Most Specific Method“, aber es macht mir Kopfschmerzen.

denke ich, dass die Begründung wäre, dass void doIt(int[]) nicht mehr spezifisch (oder umgekehrt) als void doIt(double[]) weil int[] kein Subtyp von double[] (und umgekehrt) ist. Da die beiden Überladungen gleich spezifisch sind, ist der Aufruf mehrdeutig.

Dagegen void doItAgain(int) ist spezifischer als void doItAgain(double) weil int ist ein Subtyp von double dem der JLS nach. Daher ist ein Anruf an doItAgain(42) nicht mehrdeutig.

EDIT 2 - @finnw ist richtig, es ist ein Bug. Betrachten wir diesen Teil von 15.12.2.5 (edited nicht anwendbar Fällen entfernen):

Eine Variable arity Mitglied Methode mit dem Namen m spezifischere als eine andere Variable arity Mitglied Verfahren mit dem gleichen Namen ist, wenn:

Eine Mitgliedsmethode hat n Parameter und die andere hat k Parameter, wobei n ≥ k. Die Typen der Parameter der ersten Mitgliedsmethode sind T1,. . . , Tn-1, Tn [], die Typen der Parameter der anderen Methode sind U1,. . . , Uk-1, Großbritannien []. Sei Si = Ui, 1 < = i < = k.Dann:

  • für alle j von 1 bis k-1, Tj <: Sj, und
  • für alle j von k bis n, Tj <: Sk

Wenden Sie diese zu dem Fall, wo n = k = 1, und wir sehen, dass doIt(int[]) spezifischer ist als doIt(double[]).


In der Tat gibt es eine bug report für diesen und Sun erkennt an, dass es in der Tat ein Fehler ist, obwohl sie es als Priorität haben „sehr niedrig“ . Der Fehler ist jetzt in Java 7 (b123) als behoben markiert.

+3

durch diese Argumentation sollten doIt (int) und doIt (double) auch mehrdeutig sein (ohne die varargs). – Thilo

+2

Es ist die Interaktion mit den Varargs, die diese zweideutigen, IIRC macht. Sie haben tatsächlich zwei Ebenen der Förderung, 1) Förderung einer Folge von Argumenten zu einem Array und 2) Förderung von Integral Literalen zu doubles. –

+2

Ich denke, das ist ein Fehler (dh das Verhalten des Compilers ist nicht konsistent mit der JLS.) Wenn ich diesen Abschnitt der JLS richtig verstehe, sollte 'doIt (int ...)' streng spezifischer sein als 'doIt (double. ..) 'weil' int' ein richtiger Subtyp von 'double' ist. Es ist richtig, dass 'int []' kein Subtyp von 'double []' ist, aber das ist keine der Anforderungen, daher sollte es die Überladungsauflösung nicht beeinflussen. – finnw

4

Interessant. Glücklicherweise gibt es ein paar verschiedene Möglichkeiten, um dieses Problem zu vermeiden:

Sie können die Wrapper-Typen statt in die Methodensignaturen verwenden:

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

Alternativ können Sie Generika verwenden:

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

Sie können Generika nicht verwenden, weil Sie dann nur eine Methode haben. Vermutlich macht der Code etwas anderes für Doubles als für Ints. – Thilo

+0

Guter Punkt. Ich bleibe bei der Verwendung von Wrapper-Typen in der Signatur dann: :) –

+2

Interessanter Hack. Es funktioniert, da das automatische Boxing und die Heraufstufung nie auf dasselbe Argument angewendet werden. Wenn Sie also das automatische Boxing erzwingen, verhindern Sie, dass die Double-to-Double-Promotion erfolgt und nur eine Methodensignatur übereinstimmt. Dies funktioniert * nicht * zum Beispiel # 3 (mit den 'float' Werten.) – finnw