4.3 Methode mit variabler Argumentanzahl (Varargs)
Bei vielen Methoden ist klar, wie viele Argumente exakt übergeben werden müssen; einer Methode Math.random() darf nichts übergeben werden, und bei Math.sin(double) ist genau ein Argument gültig.
4.3.1 System.out.printf(…) nimmt eine beliebige Anzahl von Argumenten an
Es gibt auch Methoden, bei denen die Anzahl gültiger Argumente prinzipiell unbeschränkt ist. Ein Beispiel ist printf(String, …), das verpflichtend einen String als Erstes erwartet, dann aber frei darin ist, was sonst übergeben wird. Gültig sind zum Beispiel:
Aufruf | Variable Argumentliste |
---|---|
System.out.printf("%n") | Ist leer. |
System.out.printf("%s", "Eins") | Besteht aus nur einem Element: "Eins". |
Besteht aus drei Elementen: "1", "2", "3". |
Um die Anzahl der Parameter beliebig zu gestalten, sieht Java Methoden mit variabler Argumentanzahl vor, Varargs genannt – in anderen Programmiersprachen heißen sie variadische Funktion. Die Methode printf(formatierungsstring, arg1, args2, arg3, …) ist so eine Varargs-Methode.
class java.io.PrintStream extends FilterOutputStream
implements Appendable, Closeable
PrintStream printf(String format, Object… args)
Nimmt eine beliebige Liste von Argumenten an und formatiert sie nach dem gegebenen Formatierungs-String format. Der Formatierungs-String bestimmt, wie viele Argumente nötig sind. Der Compiler wertet den String aber nicht aus, kann also die Korrektheit – dass die Anzahl stimmt – auch zur Compilezeit nicht prüfen.
Eine Methode mit variabler Argumentanzahl nutzt die Ellipse (...) zur Verdeutlichung, dass eine beliebige Anzahl Argumente angegeben werden darf. Dazu zählt auch die Angabe keines Elements. Der Typ fällt dabei aber nicht unter den Tisch; er wird ebenfalls angegeben.
4.3.2 Durchschnitt finden von variablen Argumenten
Wir haben vorher eine Methode avg(double[] array) geschrieben, die den arithmetischen Mittelwert von Werten berechnet. Den Parametertyp können wir nun ändern in avg(double… array), sodass die Methode einfach mit variablen Argumenten gefüllt werden kann.
Ausprogrammiert sieht das wie folgt aus; es gibt nur eine kleine Änderung von [] in …, sonst ändert sich an der Implementierung nichts:
public class AvgVarArgs {
static double avg( double... array ) { /* Implementierung wie vorher */ }
public static void main( String[] args ) {
System.out.println( avg(1, 2, 9, 3) ); // 3.75
}
}
[»] Hinweis
Werden variable Argumentlisten in der Signatur definiert, so dürfen sie nur den letzten Parameter bilden; andernfalls könnte der Compiler bei den Parametern nicht unbedingt zuordnen, was nun ein Vararg und was schon der nächste gefüllte Parameter ist. Das bedingt automatisch, dass es nur maximal ein Vararg in der Parameterliste geben kann.
Der Zusammenhang zwischen Vararg und Array
Eine Methode mit Vararg ist im Kern eine Methode mit einem Array als Parametertyp. Im Bytecode steht nicht wirklich avg(double… array), sondern avg(double[] array) mit der Zusatzinfo, dass array ein Vararg ist, damit der Compiler beliebig viele Argumente und nicht ausschließlich ein double[]-Array als Argument erlaubt.
Der Nutzer kann eine Varargs-Methode aufrufen, ohne ein Array für die Argumente explizit zu definieren. Er bekommt auch gar nicht mit, dass der Compiler im Hintergrund ein Array mit vier Elementen angelegt hat. So generiert der Compiler aus
System.out.println( avg(1, 2, 9, 3) );
Folgendes:
System.out.println( avg( new double[] { 1, 2, 9, 3 } ) );
An der Schreibweise lässt sich gut ablesen, dass wir ein Array auch von Hand übergeben können:
double[] values = { 1, 2, 9, 3 };
System.out.println( avg( values ) );
[»] Hinweis
Da Varargs als Arrays umgesetzt werden, sind überladene Varianten wie avg(int… array) und avg(int[] array), also einmal mit einem Vararg und einmal mit einem Array, nicht möglich. Besser ist es hier, immer eine Variante mit Varargs zu nehmen, da sie mächtiger ist. Einige Autoren schreiben auch die Einstiegsmethode main(String[] args) mit variablen Argumenten, also main(String… args). Das ist gültig, denn im Bytecode steht ja ein Array.
4.3.3 Varargs-Designtipps *
Hat eine Methode nur einen Array-Parameter und steht er noch am Ende, so kann er relativ einfach durch ein Vararg ersetzt werden. Das gibt dem Aufrufer die komfortable Möglichkeit, eine kompaktere Syntax zu nutzen. Unsere main(String[] args)-Methode kann auch als main(String... args) deklariert werden, sodass der main(…)-Methode bei Tests einfach variable Argumente übergeben werden können.
Muss eine Mindestanzahl von Argumenten garantiert werden – bei max(…) sollten das mindestens zwei sein –, ist es besser, eine Deklaration wie folgt zu nutzen: max(int first, int second, int... remaining).
Aus Performance-Gründen ist es empfehlenswert, Methoden mit häufigen Parameterlistengrößen als feste Methoden anzubieten, etwa max(double, double), max(double, double, double) und dann max(double...). Der Compiler wählt automatisch immer die passende Methode aus, für zwei oder drei Parameter sind keine temporären Array-Objekte nötig, und die automatische Speicherbereinigung muss nichts wegräumen.