Quand utilisez-vous varargs en Java?

196

J'ai peur des varargs. Je ne sais pas comment les utiliser.

De plus, il est dangereux de laisser les gens passer autant d'arguments qu'ils le souhaitent.

Quel est un exemple de contexte qui serait un bon endroit pour les utiliser?

Harry Quince
la source
3
Je ne vois pas pourquoi ce serait «dangereux». Ce n'est pas plus dangereux que d'avoir une méthode appelée un grand nombre de fois avec des arguments différents. Avez-vous des problèmes avec la pile? Alors vous ne devriez pas, car les varargs sont mappés à des tableaux qui sont passés par référence.
jfpoilpret
66
Je voulais juste intervenir: il n'y a rien de mal à éviter les fonctionnalités de langage avec lesquelles vous n'êtes pas (encore) entièrement à l'aise. Bien mieux que d'utiliser des fonctionnalités que vous ne comprenez pas! ;)
Steven
2
Il est normal d'avoir peur de l'inconnu. Pour en savoir plus, lisez sur les varargs ici docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Vous verrez que les varargs ne sont pas à craindre. Les Varargs sont utiles. Savoir utiliser varargs vous donne la possibilité d'écrire des méthodes comme [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Développeur Marius Žilėnas
1
Ce n'est pas une critique de varargs, mais il y a en fait une raison d '«avoir peur» (jusqu'à ce que vous compreniez précisément les limites de varargs); c'est pourquoi l'annotation @SafeVarargs existe. stackoverflow.com/a/14252221/1593924
Jon Coombs

Réponses:

153

Les Varargs sont utiles pour toute méthode qui doit traiter un nombre indéterminé d'objets . Un bon exemple est String.format. La chaîne de format peut accepter n'importe quel nombre de paramètres, vous avez donc besoin d'un mécanisme pour transmettre n'importe quel nombre d'objets.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Andy White
la source
5
Un paramètre de tableau peut également recevoir un nombre indéterminé d'objets, mais un paramètre varargs permet plus de flexibilité et de commodité au moment de l'appel. Vous pouvez écrire du code pour créer un tableau et le transmettre, ou vous pouvez laisser Java le faire pour vous lorsque vous choisissez de le recevoir dans un paramètre varargs.
H2ONaCl
79

Une bonne règle d'or serait:

"Utilisez varargs pour toute méthode (ou constructeur) qui a besoin d'un tableau de T (quel que soit le type T) comme entrée".

Cela facilitera les appels à ces méthodes (inutile de le faire new T[]{...}).

Vous pouvez étendre cette règle pour inclure des méthodes avec un List<T>argument, à condition que cet argument soit uniquement pour l'entrée (c'est-à-dire que la liste n'est pas modifiée par la méthode).

De plus, je m'abstiendrai d'utiliser f(Object... args)parce que cela glisse vers une méthode de programmation avec des API peu claires.

En termes d'exemples, je l'ai utilisé dans DesignGridLayout , où je peux ajouter plusieurs JComponents en un seul appel:

layout.row().grid(new JLabel("Label")).add(field1, field2, field3);

Dans le code ci-dessus, la méthode add () est définie comme add(JComponent... components).

Enfin, l'implémentation de telles méthodes doit prendre en compte le fait qu'elle peut être appelée avec un vararg vide! Si vous souhaitez imposer au moins un argument, vous devez utiliser une astuce moche telle que:

void f(T arg1, T... args) {...}

Je considère cette astuce moche car la mise en œuvre de la méthode sera moins simple que de l'avoir juste T... argsdans sa liste d'arguments.

Espère que cela aide à clarifier le point sur les varargs.

jfpoilpret
la source
1
Avez-vous if (args.length == 0) throw new RuntimeException("foo");plutôt envisagé d'ajouter une vérification des conditions préalables ? (Puisque l'appelant violait le contrat)
Micha Wiedenmann
24
Eh bien, le but d'une bonne API est d'éviter les abus le plus tôt possible, donc au moment de la compilation, lorsque cela est possible, d'où la suggestion pour void f(T arg1, T... args)cela garantit toujours qu'elle n'est jamais appelée sans argument, sans avoir besoin d'attendre l'exécution.
jfpoilpret
Je pense que la plupart du temps, un appel à une fonction varargs uniquement sans aucun argument reviendra à ne rien faire du tout. Le fait est que ne fournir aucun argument à une fonction varargs ne causera probablement pas de dommages considérables.
WorldSEnder
Les varargs sont utiles, mais ils ne sont pas équivalents à l'utilisation d'un tableau. Les Varargs ne sont pas réifiables. Ainsi, comme les génériques, ils sont affectés par l'effacement de type qui pourrait très bien être une contrainte inacceptable selon le travail.
Julian
34

J'utilise fréquemment des varargs pour la sortie dans les journaux à des fins de débogage.

Presque toutes les classes de mon application ont une méthode debugPrint ():

private void debugPrint(Object... msg) {
    for (Object item : msg) System.out.print(item);
    System.out.println();
}

Ensuite, dans les méthodes de la classe, j'ai des appels comme celui-ci:

debugPrint("for assignment ", hwId, ", student ", studentId, ", question ",
    serialNo, ", the grade is ", grade);

Lorsque je suis convaincu que mon code fonctionne, je commente le code dans la méthode debugPrint () afin que les journaux ne contiennent pas trop d'informations superflues et indésirables, mais je peux laisser les appels individuels à debugPrint () sans commentaire. Plus tard, si je trouve un bogue, je décommente simplement le code debugPrint () et tous mes appels à debugPrint () sont réactivés.

Bien sûr, je pourrais tout aussi facilement éviter les varargs et faire ce qui suit à la place:

private void debugPrint(String msg) {
    System.out.println(msg);
}

debugPrint("for assignment " + hwId + ", student " + studentId + ", question "
    + serialNo + ", the grade is " + grade);

Cependant, dans ce cas, lorsque je commente le code debugPrint (), le serveur doit encore se donner la peine de concaténer toutes les variables à chaque appel à debugPrint (), même si rien n'est fait avec la chaîne résultante. Si j'utilise des varargs, cependant, le serveur n'a qu'à les mettre dans un tableau avant de se rendre compte qu'il n'en a pas besoin. Beaucoup de temps est économisé.

bob
la source
4
Vous pouvez implémenter une méthode de débogage dans la superclasse pour imprimer n'importe quel objet en utilisant la réflexion.
Lluis Martinez
12

Varargs peut être utilisé lorsque nous ne sommes pas sûrs du nombre d'arguments à passer dans une méthode. Il crée un tableau de paramètres de longueur non spécifiée en arrière-plan et un tel paramètre peut être traité comme un tableau à l'exécution.

Si nous avons une méthode surchargée pour accepter un nombre différent de paramètres, alors au lieu de surcharger la méthode plusieurs fois, nous pouvons simplement utiliser le concept varargs.

De même, lorsque le type des paramètres variera, l'utilisation de "Object ... test" simplifiera beaucoup le code.

Par exemple:

public int calculate(int...list) {
    int sum = 0;
    for (int item : list) {
        sum += item;
    }
    return sum;
}

Ici indirectement un tableau de type int (liste) est passé en paramètre et est traité comme un tableau dans le code.

Pour une meilleure compréhension suivez ce lien (cela m'a beaucoup aidé à comprendre clairement ce concept): http://www.javadb.com/using-varargs-in-java

PS: Même moi, j'avais peur d'utiliser des varargs quand je ne le savais pas. Mais maintenant j'y suis habitué. Comme il est dit: "Nous nous accrochons au connu, peur de l'inconnu", alors utilisez-le autant que vous le pouvez et vous aussi commencerez à l'aimer :)

Sarvan
la source
4
C # L'équivalent de varargs est "params". Il fait également la même chose et accepte un nombre variable de paramètres. Voir ceci pour une meilleure compréhension: dotnetperls.com/params
Sarvan
12

Varargs est la fonctionnalité ajoutée dans la version java 1.5.

Pourquoi utiliser ça?

  1. Et si, vous ne connaissez pas le nombre d'arguments à passer pour une méthode?
  2. Que faire si vous souhaitez transmettre un nombre illimité d'arguments à une méthode?

Comment ça marche?

Il crée un tableau avec les arguments donnés et passe le tableau à la méthode.

Exemple :

public class Solution {



    public static void main(String[] args) {
        add(5,7);
        add(5,7,9);
    }

    public static void add(int... s){
        System.out.println(s.length);
        int sum=0;
        for(int num:s)
            sum=sum+num;
        System.out.println("sum is "+sum );
    }

}

Production :

2

la somme est de 12

3

la somme est 21

Arun Prakash
la source
6

J'ai aussi une peur liée aux varargs:

Si l'appelant passe un tableau explicite à la méthode (par opposition à plusieurs paramètres), vous recevrez une référence partagée à ce tableau.

Si vous avez besoin de stocker ce tableau en interne, vous souhaiterez peut-être le cloner d'abord pour éviter que l'appelant ne puisse le modifier ultérieurement.

 Object[] args = new Object[] { 1, 2, 3} ;

 varArgMethod(args);  // not varArgMethod(1,2,3);

 args[2] = "something else";  // this could have unexpected side-effects

Bien que ce ne soit pas vraiment différent du passage de tout type d'objet dont l'état pourrait changer plus tard, puisque le tableau est généralement (dans le cas d'un appel avec plusieurs arguments au lieu d'un tableau) un nouveau créé par le compilateur en interne que vous pouvez en toute sécurité utilisation, c'est certainement un comportement inattendu.

Thilo
la source
1
Correct, mais cet exemple semble un peu tiré par les cheveux. Est-ce que les gens vont probablement utiliser votre API de cette façon?
jfpoilpret
2
On ne sait jamais ... Et surtout quand le nombre d'arguments n'est pas codé en dur côté appelant, mais aussi collecté par exemple dans une liste en boucle, le passage dans un tableau n'est pas si rare.
Thilo
5
Je pense que votre exemple de cas d’utilisation n’est généralement pas improbable. On pourrait dire que votre API ne devrait pas utiliser le tableau d'entrée de cette manière, et si c'est le cas, elle doit le documenter.
Lawrence Dol
surcharger la méthode pour prendre en charge les deux; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman
2
@thetoolman: Je ne pense pas que ce soit possible. Elle sera considérée comme une méthode en double.
Thilo
1

J'utilise fréquemment varargs pour les constructeurs qui peuvent prendre une sorte d'objet filtre. Par exemple, une grande partie de notre système basé sur Hadoop est basé sur un mappeur qui gère la sérialisation et la désérialisation des éléments vers JSON, et applique un certain nombre de processeurs qui prennent chacun un élément de contenu et le modifient et le renvoient, ou retournent null rejeter.

Kevin Peterson
la source
1

Dans la documentation Java de Var-Args, l'utilisation de var args est assez claire:

http://docs.oracle.com/javase/1.5.0/docs/guide/language/varargs.html

à propos de l'utilisation, il dit:

"Alors, quand devriez-vous utiliser varargs? En tant que client, vous devriez en profiter chaque fois que l'API les propose. Les principales utilisations des API principales incluent la réflexion, la mise en forme des messages et la nouvelle fonction printf. En tant que concepteur d'API, vous devez les utiliser avec parcimonie, seulement lorsque l'avantage est vraiment convaincant. En règle générale, vous ne devez pas surcharger une méthode varargs, sinon il sera difficile pour les programmeurs de déterminer quelle surcharge est appelée. "

Surendra
la source