Java Varargs (Argomenti a Lunghezza Variabile)
Definisci metodi Java che accettano qualsiasi numero di argomenti con varargs (Type... name). Impara le regole, le convenzioni di chiamata, l'overloading e i problemi comuni.
Un parametro varargs (abbreviazione di variable-length arguments) consente a un metodo di accettare qualsiasi numero di argomenti — zero, uno, dieci, cento — dello stesso tipo, senza obbligare il chiamante a raggrupparli prima in un array. Si scrive l'ultimo parametro come Type... name, e all'interno del metodo name è un normale array di quel tipo.
Hai usato metodi varargs per anni. String.format("%s + %s = %s", a, b, c) accetta tanti valori di sostituzione quanti ne richiede la stringa di formato. List.of(1, 2, 3, 4, 5) accetta un numero arbitrario di elementi. Entrambi si basano su varargs.
Dichiarare un parametro varargs
La sintassi prevede tre punti tra il tipo dell'elemento e il nome del parametro:
public static int sum(int... numbers) {
int total = 0;
for (int n : numbers) total += n;
return total;
}I chiamanti possono passare qualsiasi numero di argomenti int:
sum(); // 0
sum(5); // 5
sum(1, 2, 3); // 6
sum(1, 2, 3, 4); // 10All'interno del metodo, numbers è un int[]. Il compilatore raggruppa gli argomenti separati in un array nel punto di chiamata.
Varargs è semplicemente un array
Il corpo del metodo tratta il parametro varargs esattamente come un array — .length, indicizzazione, for-each, tutto:
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("-"); // ""Poiché il parametro è un array, è possibile anche passare direttamente un array letterale:
String[] colors = {"red", "green", "blue"};
join(", ", colors); // same as join(", ", "red", "green", "blue")Il compilatore accetta entrambe le forme.
Le regole
Alcune limitazioni accompagnano la sintassi:
- Un metodo può avere al massimo un parametro varargs. Due sarebbero ambigui.
- Deve essere l'ultimo parametro. Altrimenti il compilatore non potrebbe determinare dove finiscono gli argomenti variabili e dove inizia il parametro successivo.
- Gli altri parametri vengono prima. I parametri fissi in testa, varargs in coda.
// 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 lastChiamare metodi varargs
È possibile passare:
- Valori separati —
f(1, 2, 3). - Un array del tipo corretto —
f(new int[]{1, 2, 3}). - Niente —
f(). Il metodo riceve un array di lunghezza 0, nonnull.
sum(); // numbers is int[0]
sum(new int[0]); // also int[0]
sum(new int[]{1, 2, 3}); // numbers is int[]{1,2,3}Un argomento null è un caso insidioso: sum(null) compila, tratta null come l'array stesso, e il ciclo lancia NullPointerException. La maggior parte dei metodi varargs non si aspetta null.
Varargs e overloading
Quando si fa l'overloading di un metodo, le versioni varargs vengono verificate per ultime. Viene preferito un overload non varargs (a arità fissa) che corrisponde:
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"Questo è corretto — varargs è il fallback, usato solo quando non c'è nulla di più specifico.
Un problema comune: Object...
Varargs di Object accetta qualsiasi cosa, incluso un array:
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;@..."Il compilatore sceglie l'interpretazione che produce una chiamata valida. Con Object..., entrambe le interpretazioni funzionano, e le regole propendono verso "trattare l'array come l'array varargs". Se si vuole che l'array sia un singolo elemento, si deve fare il cast: log((Object) names).
Quando usare (e quando non usare) varargs
Varargs è una comodità per chi chiama il metodo, non un'astrazione gratuita. Ogni chiamata che passa valori separati alloca un nuovo array in background. Per metodi chiamati in cicli stretti su un percorso critico, quell'allocazione può accumularsi — motivo per cui la libreria standard a volte fornisce overload a arità fissa accanto alla versione varargs. List.of è l'esempio classico: ha overload dedicati per of(), of(e1), of(e1, e2) fino a dieci elementi, e solo oltre quel limite ricade su varargs of(E... elements).
Per il codice comune il costo è trascurabile e la chiarezza vince. Usa varargs quando:
- il numero di argomenti varia genuinamente (formattazione, logging, costruzione di collezioni);
- obbligare il chiamante a scrivere
new Type[]{...}sarebbe rumore.
Preferisci un array semplice o un parametro List quando il chiamante di solito ha già una collezione a disposizione, o quando vuoi rendere "passare molti valori" il caso esplicito e atteso.
Un esempio pratico
Cosa viene dopo
Ora puoi scrivere metodi con tutta la gamma di forme di parametri che Java offre — fissi, con overloading, ricorsivi e varargs. L'ultimo capitolo della sezione sui metodi torna all'unico metodo presente in ogni programma: il metodo main — la cui String[] args può essere scritta ugualmente String... args — e cosa dice veramente la sua firma alla JVM.