Java Varargs (Variable-Length Arguments)
Java-Methoden mit beliebig vielen Argumenten via Varargs (Type... name) definieren. Regeln, Aufrufkonventionen, Überladung und häufige Fallstricke.
Ein Varargs-Parameter (kurz für variable-length arguments) erlaubt einer Methode, beliebig viele Argumente — null, eins, zehn, hundert — desselben Typs entgegenzunehmen, ohne dass der Aufrufer sie zuvor in ein Array verpacken muss. Den letzten Parameter schreibt man als Type... name, und innerhalb der Methode ist name ein gewöhnliches Array dieses Typs.
Sie haben Varargs-Methoden schon jahrelang verwendet. String.format("%s + %s = %s", a, b, c) nimmt beliebig viele Ersetzungswerte entgegen, die der Formatstring benötigt. List.of(1, 2, 3, 4, 5) nimmt eine beliebige Anzahl von Elementen an. Beide basieren auf Varargs.
Einen Varargs-Parameter deklarieren
Die Syntax sind drei Punkte zwischen dem Elementtyp und dem Parameternamen:
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) total += n;
return total;
}Aufrufer übergeben beliebig viele int-Argumente:
sum(); // 0
sum(5); // 5
sum(1, 2, 3); // 6
sum(1, 2, 3, 4); // 10Innerhalb der Methode ist numbers ein int[]. Der Compiler bündelt die einzelnen Argumente an der Aufrufstelle zu einem Array.
Varargs ist nur ein Array
Der Methodenrumpf behandelt den Varargs-Parameter genau wie ein Array — .length, Indexzugriff, for-each, alles:
public static String join(String sep, String... parts) {
if (parts.length == 0) return "";
StringBuilder sb = new StringBuilder(parts[0]);
for (int i = 1; i < parts.length; i++) {
sb.append(sep).append(parts[i]);
}
return sb.toString();
}
join(", ", "red", "green", "blue"); // "red, green, blue"
join("-"); // ""Da der Parameter ein Array ist, kann man auch direkt ein Array-Literal übergeben:
String[] colors = {"red", "green", "blue"};
join(", ", colors); // same as join(", ", "red", "green", "blue")Der Compiler akzeptiert beide Formen.
Die Regeln
Mit der Syntax gehen einige Einschränkungen einher:
- Eine Methode darf höchstens einen Varargs-Parameter haben. Zwei wären mehrdeutig.
- Er muss der letzte Parameter sein. Andernfalls könnte der Compiler nicht erkennen, wo die variablen Argumente enden und der nächste Parameter beginnt.
- Andere Parameter kommen zuerst. Feste Parameter vorne, Varargs am Ende.
// VALID
public static String tag(String name, String... attrs) { ... }
public static int sum(int initial, int... rest) { ... }
// INVALID
// public static int sum(int... a, int... b) // two varargs
// public static int sum(int... a, int b) // varargs not lastVarargs-Methoden aufrufen
Man kann übergeben:
- Einzelne Werte —
f(1, 2, 3). - Ein Array des richtigen Typs —
f(new int[]{1, 2, 3}). - Gar nichts —
f(). Die Methode erhält ein Array der Länge 0, nichtnull.
sum(); // numbers is int[0]
sum(new int[0]); // also int[0]
sum(new int[]{1, 2, 3}); // numbers is int[]{1,2,3}Ein null-Argument ist ein Sonderfall: sum(null) kompiliert, behandelt null als das Array selbst, und die Schleife wirft eine NullPointerException. Die meisten Varargs-Methoden erwarten kein null.
Varargs und Überladung
Wenn man eine Methode überlädt, werden Varargs-Varianten zuletzt geprüft. Eine nicht-varargs (fest-stellige) Überladung, die passt, wird bevorzugt:
public static void show(int a, int b) { System.out.println("two ints"); }
public static void show(int... xs) { System.out.println("varargs"); }
show(1, 2); // "two ints" — fixed-arity wins
show(1); // "varargs" — no fixed match
show(1, 2, 3); // "varargs"Das ist gewollt — Varargs ist der Rückfallmechanismus und wird nur verwendet, wenn nichts Spezifischeres passt.
Ein häufiger Fallstrick: Object...
Varargs mit Object akzeptiert alles, einschließlich Arrays:
public static void log(Object... values) {
for (Object v : values) System.out.println(v);
}
log("a", 1, 2.5); // 3 calls
String[] names = {"Ada", "Bob"};
log(names); // ONE Object[] argument, prints "Ada" then "Bob"
log((Object) names); // ONE Object argument, prints "[Ljava.lang.String;@..."Der Compiler wählt diejenige Interpretation, die einen gültigen Aufruf ergibt. Bei Object... funktionieren beide Interpretationen, und die Regeln tendieren dazu, „das Array als Varargs-Array zu behandeln." Wenn man das Array als einzelnes Element übergeben möchte, muss man es casten: log((Object) names).
Wann man Varargs (nicht) verwenden sollte
Varargs ist eine Erleichterung an der Aufrufstelle, keine kostenlose Abstraktion. Jeder Aufruf, der einzelne Werte übergibt, alloziert im Hintergrund ein neues Array. Bei Methoden, die in engen Schleifen auf einem heißen Pfad aufgerufen werden, kann diese Allokation ins Gewicht fallen — weshalb die Standardbibliothek manchmal fest-stellige Überladungen neben der Varargs-Version anbietet. List.of ist das klassische Beispiel: Es gibt dedizierte Überladungen für of(), of(e1), of(e1, e2) bis zu zehn Elementen, und erst darüber hinaus wird auf ein Varargs of(E... elements) zurückgegriffen.
Für alltäglichen Code sind die Kosten vernachlässigbar, und Klarheit gewinnt. Verwende Varargs, wenn:
- die Anzahl der Argumente tatsächlich variiert (Formatierung, Logging, Aufbau von Collections);
- es umständlich wäre, den Aufrufer
new Type[]{...}schreiben zu lassen.
Bevorzuge einen einfachen Array- oder List-Parameter, wenn der Aufrufer normalerweise schon eine Collection zur Hand hat, oder wenn man „viele Werte übergeben" als expliziten, erwarteten Fall deutlich machen möchte.
Ein ausgearbeitetes Beispiel
Wie es weitergeht
Sie können jetzt Methoden mit allen Parametervarianten schreiben, die Java bietet — feste, überladene, rekursive und Varargs. Das letzte Kapitel des Methoden-Teils kehrt zur einen Methode zurück, die jedes Programm hat: der main-Methode — deren String[] args ebenso als String... args geschrieben werden kann — und was ihre Signatur der JVM wirklich mitteilt.