W3docs

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);    // 10

All'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 last

Chiamare metodi varargs

È possibile passare:

  1. Valori separatif(1, 2, 3).
  2. Un array del tipo correttof(new int[]{1, 2, 3}).
  3. Nientef(). Il metodo riceve un array di lunghezza 0, non null.
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

java— editable, runs on the server

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.

Esercitati

Pratica
All'interno di un metodo dichiarato come void f(int... xs), qual è il tipo di xs quando non vengono passati argomenti?
All'interno di un metodo dichiarato come void f(int... xs), qual è il tipo di xs quando non vengono passati argomenti?
Was this page helpful?