W3docs

Java Varargs (Arguments à longueur variable)

Définissez des méthodes Java acceptant n'importe quel nombre d'arguments avec les varargs (Type... nom). Règles, conventions d'appel et pièges courants.

Un paramètre varargs (abréviation de variable-length arguments, arguments à longueur variable) permet à une méthode d'accepter n'importe quel nombre d'arguments — zéro, un, dix, cent — du même type, sans forcer l'appelant à les regrouper dans un tableau au préalable. On écrit le dernier paramètre sous la forme Type... name, et à l'intérieur de la méthode name est un tableau ordinaire de ce type.

Vous utilisez des méthodes varargs depuis des années. String.format("%s + %s = %s", a, b, c) accepte autant de valeurs de substitution que la chaîne de format en a besoin. List.of(1, 2, 3, 4, 5) accepte un nombre arbitraire d'éléments. Les deux s'appuient sur les varargs.

Déclarer un paramètre varargs

La syntaxe utilise trois points entre le type de l'élément et le nom du paramètre :

public static int sum(int... numbers) {
  int total = 0;
  for (int n : numbers) total += n;
  return total;
}

Les appelants passent n'importe quel nombre d'arguments int :

sum();              // 0
sum(5);             // 5
sum(1, 2, 3);       // 6
sum(1, 2, 3, 4);    // 10

À l'intérieur de la méthode, numbers est un int[]. Le compilateur regroupe les arguments isolés dans un tableau au niveau du site d'appel.

Les varargs ne sont qu'un tableau

Le corps de la méthode traite le paramètre varargs exactement comme un tableau — .length, indexation, for-each, tout :

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("-");                            // ""

Le paramètre étant un tableau, on peut également passer directement un tableau littéral :

String[] colors = {"red", "green", "blue"};
join(", ", colors);                   // same as join(", ", "red", "green", "blue")

Le compilateur accepte l'une ou l'autre forme.

Les règles

Quelques contraintes accompagnent la syntaxe :

  • Une méthode ne peut avoir qu'un seul paramètre varargs. Deux seraient ambigus.
  • Il doit être le dernier paramètre. Sinon le compilateur ne pourrait pas savoir où se terminent les arguments variables et où commence le paramètre suivant.
  • Les autres paramètres viennent en premier. Les paramètres fixes au début, les varargs à la fin.
// 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

Appeler des méthodes varargs

On peut passer :

  1. Des valeurs isoléesf(1, 2, 3).
  2. Un tableau du bon typef(new int[]{1, 2, 3}).
  3. Rien du toutf(). La méthode reçoit un tableau de longueur 0, pas 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 argument null est un cas délicat : sum(null) compile, traite null comme le tableau lui-même, et la boucle lève une NullPointerException. La plupart des méthodes varargs ne s'attendent pas à recevoir null.

Varargs et surcharge

Lorsqu'on surcharge une méthode, les versions varargs sont vérifiées en dernier. Une surcharge sans varargs (arité fixe) correspondant à l'appel sera préférée :

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"

C'est une bonne chose — les varargs sont le dernier recours, utilisés uniquement quand rien de plus spécifique ne correspond.

Un piège courant : Object...

Les varargs de type Object acceptent tout, y compris un tableau :

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;@..."

Le compilateur choisit l'interprétation qui produit un appel valide. Avec Object..., les deux interprétations fonctionnent, et les règles penchent vers « traiter le tableau comme le tableau varargs ». Si l'on veut le tableau comme élément unique, il faut le caster : log((Object) names).

Quand (ne pas) utiliser les varargs

Les varargs sont une commodité au niveau du site d'appel, pas une abstraction gratuite. Chaque appel passant des valeurs isolées alloue un nouveau tableau en arrière-plan. Pour les méthodes appelées dans des boucles serrées sur un chemin critique, cette allocation peut s'accumuler — c'est pourquoi la bibliothèque standard fournit parfois des surcharges à arité fixe en parallèle de la version varargs. List.of en est l'exemple classique : elle dispose de surcharges dédiées pour of(), of(e1), of(e1, e2) jusqu'à dix éléments, et ne bascule sur un of(E... elements) varargs qu'au-delà.

Pour le code courant, le coût est négligeable et la clarté l'emporte. Utilisez les varargs quand :

  • le nombre d'arguments varie réellement (formatage, journalisation, construction de collections) ;
  • obliger l'appelant à écrire new Type[]{...} serait du bruit superflu.

Préférez un tableau simple ou un paramètre List quand l'appelant dispose déjà généralement d'une collection, ou quand vous souhaitez que « passer plusieurs valeurs » soit le cas explicite et attendu.

Un exemple complet

java— editable, runs on the server

La suite

Vous pouvez désormais écrire des méthodes avec toutes les formes de paramètres que Java offre — fixes, surchargées, récursives et varargs. Le dernier chapitre de la partie sur les méthodes revient sur la méthode que tout programme possède : la méthode main — dont String[] args peut tout aussi bien s'écrire String... args — et ce que sa signature indique réellement à la JVM.

Pratique

Pratique
Dans une méthode déclarée void f(int... xs), quel est le type de xs quand aucun argument n'est passé ?
Dans une méthode déclarée void f(int... xs), quel est le type de xs quand aucun argument n'est passé ?
Was this page helpful?