Quando usi varargs in Java?


192

Ho paura dei vararg. Non so per cosa usarli.

Inoltre, è pericoloso lasciare che le persone passino tutti gli argomenti che vogliono.

Qual è un esempio di un contesto che sarebbe un buon posto per usarli?


3
Non vedo perché sarebbe "pericoloso". Non è più pericoloso che avere un metodo chiamato un gran numero di volte con argomenti diversi. Hai dei dubbi con lo stack? Quindi non dovresti, perché i vararg sono mappati su array che sono passati per riferimento.
jfpoilpret,

66
Volevo solo andare in giro: non c'è niente di sbagliato nell'evitare funzionalità linguistiche con cui non si è (ancora) del tutto a proprio agio. Molto meglio dell'uso di funzionalità che non capisci! ;)
Steven

2
È normale temere l'ignoto. Per saperne di più leggi su varargs qui docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Vedrai che i vararg non sono nulla di cui aver paura. Varargs sono utili. Saper usare varargs ti dà la possibilità di scrivere metodi come [PrintStream.format] (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Sviluppatore Marius Žilėnas,

1
Questa non è una critica a Varargs, ma in realtà c'è qualche motivo per "avere paura" (fino a quando non capirai esattamente i limiti di Varargs); ecco perché esiste l'annotazione @SafeVarargs. stackoverflow.com/a/14252221/1593924
Jon Coombs,

Risposte:


150

I Vararg sono utili per qualsiasi metodo che deve affrontare un numero indeterminato di oggetti . Un buon esempio è String.format. La stringa di formato può accettare qualsiasi numero di parametri, quindi è necessario un meccanismo per passare un numero qualsiasi di oggetti.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);

5
Un parametro array può anche ricevere un numero indeterminato di oggetti, ma un parametro varargs consente maggiore flessibilità e praticità nel punto di invocazione. Puoi scrivere codice per creare un array e passarlo, oppure puoi lasciare che Java lo faccia per te quando scegli di riceverlo in un parametro varargs.
H2ONaCl

79

Una buona regola empirica sarebbe:

"Usa varargs per qualsiasi metodo (o costruttore) che necessita di un array di T (qualunque sia il tipo T) come input".

Ciò renderà le chiamate a questi metodi più facili (non è necessario farlo new T[]{...}).

È possibile estendere questa regola per includere metodi con un List<T>argomento, a condizione che questo argomento sia solo per input (ovvero, l'elenco non viene modificato dal metodo).

Inoltre, mi asterrei dall'usarlo f(Object... args)perché scivola verso un modo di programmazione con API poco chiare.

In termini di esempi, l'ho usato in DesignGridLayout , dove posso aggiungere diversi messaggi JComponentin una chiamata:

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

Nel codice sopra il metodo add () è definito come add(JComponent... components).

Infine, l'implementazione di tali metodi deve tener conto del fatto che può essere chiamato con un vararg vuoto! Se vuoi imporre almeno un argomento, devi usare un brutto trucco come:

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

Considero questo trucco brutto perché l'implementazione del metodo sarà meno semplice che avere solo T... argsnella sua lista di argomenti.

Spero che questo aiuti a chiarire il punto su Varargs.


1
Hai preso in considerazione l'aggiunta di un controllo dei presupposti if (args.length == 0) throw new RuntimeException("foo");? (Poiché il chiamante stava violando il contratto)
Micha Wiedenmann,

24
Bene, lo scopo di una buona API è prevenire un uso improprio il più presto possibile, quindi al momento della compilazione quando è possibile, quindi il suggerimento per void f(T arg1, T... args)questo assicura sempre che non venga mai chiamato senza alcun argomento, senza dover aspettare fino al runtime.
jfpoilpret,

Penso che il più delle volte una chiamata a una funzione solo varargs senza argomenti equivale a non fare nulla. Il punto è che non è molto probabile che fornire alcun argomento a una funzione varargs possa arrecare gravi danni.
WorldSEnder,

I vararg sono utili, ma non equivalgono all'utilizzo di un array. I Vararg non sono riutilizzabili. Quindi, come i generici, sono interessati dalla cancellazione del tipo che potrebbe benissimo essere un vincolo inaccettabile a seconda del lavoro.
Giuliano

34

Uso varargs frequentemente per l'output nei registri ai fini del debug.

Praticamente ogni classe nella mia app ha un metodo debugPrint ():

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

Quindi, all'interno dei metodi della classe, ho chiamate come le seguenti:

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

Quando sono soddisfatto che il mio codice funzioni, commento il codice nel metodo debugPrint () in modo che i log non contengano troppe informazioni estranee e indesiderate, ma posso lasciare le singole chiamate a debugPrint () senza commenti. In seguito, se trovo un bug, decomprimo il codice debugPrint () e tutte le mie chiamate a debugPrint () vengono riattivate.

Naturalmente, potrei altrettanto facilmente evitare varargs e invece fare quanto segue:

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

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

Tuttavia, in questo caso, quando commento il codice debugPrint (), il server deve ancora affrontare il problema di concatenare tutte le variabili in ogni chiamata a debugPrint (), anche se non viene fatto nulla con la stringa risultante. Se uso varargs, tuttavia, il server deve solo inserirli in un array prima di rendersi conto di non averne bisogno. Molto tempo è risparmiato.


4
È possibile implementare un metodo di debug nella superclasse per stampare qualsiasi oggetto usando reflection.
Lluis Martinez,

12

Varargs può essere usato quando non siamo sicuri del numero di argomenti da passare in un metodo. Crea una matrice di parametri di lunghezza non specificata in background e tale parametro può essere trattato come una matrice in fase di esecuzione.

Se abbiamo un metodo che è sovraccarico per accettare un numero diverso di parametri, quindi invece di sovraccaricare il metodo diverse volte, possiamo semplicemente usare il concetto di varargs.

Anche quando il tipo di parametri varierà, l'uso di "Test oggetto ..." semplifica molto il codice.

Per esempio:

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

Qui indirettamente un array di tipo int (elenco) viene passato come parametro e viene trattato come un array nel codice.

Per una migliore comprensione segui questo link (mi ha aiutato molto a capire chiaramente questo concetto): http://www.javadb.com/using-varargs-in-java

PS: Anche io avevo paura di usare Varargs quando non lo sapevo. Ma ora ci sono abituato. Come si dice: "Ci aggrappiamo al conosciuto, abbiamo paura dell'ignoto", quindi usalo il più possibile e anche tu inizierai a piacerti :)


4
C # equivalente di varargs è "params". Fa anche la stessa cosa e accetta un numero variabile di parametri. Vedi questo per una migliore comprensione: dotnetperls.com/params
Sarvan

11

Varargs è la funzione aggiunta in Java versione 1.5.

Perché usare questo?

  1. Cosa succede se non si conosce il numero di argomenti da passare per un metodo?
  2. Cosa succede se si desidera passare un numero illimitato di argomenti a un metodo?

Come funziona?

Crea una matrice con gli argomenti indicati e passa la matrice al metodo.

Esempio :

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 );
    }

}

Produzione :

2

la somma è 12

3

la somma è 21


6

Anch'io ho una paura legata a Varargs:

Se il chiamante passa in un array esplicito al metodo (al contrario di più parametri), riceverai un riferimento condiviso a quell'array.

Se è necessario archiviare questo array internamente, è consigliabile clonarlo prima per evitare che il chiamante sia in grado di modificarlo in seguito.

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

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

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

Anche se questo non è molto diverso dal passaggio in qualsiasi tipo di oggetto il cui stato potrebbe cambiare in seguito, poiché l'array è di solito (nel caso di una chiamata con più argomenti anziché un array) uno nuovo creato internamente dal compilatore che puoi tranquillamente usare, questo è certamente un comportamento inaspettato.


1
Corretto, ma questo esempio sembra un po 'inverosimile. È probabile che le persone utilizzino la tua API in questo modo?
jfpoilpret,

2
Non si sa mai ... E soprattutto quando il numero di argomenti non è hardcoded sul lato chiamante, ma anche raccolto detto in un elenco in un ciclo, passare in un array non è poi così insolito.
Thilo,

5
Penso che il tuo esempio di caso d'uso sia, in generale, improbabile. Si potrebbe sostenere che l'API non dovrebbe utilizzare l'array di input in questo modo e, in caso affermativo, deve documentarlo.
Lawrence Dol,

sovraccaricare il metodo per supportare entrambi; argMethod (Object .. objList) argMethod (Object [] objList)
thetoolman

2
@thetoolman: non credo sia possibile. Sarà considerato un metodo duplicato.
Thilo,

1

Uso varargs frequentemente per costruttori che possono prendere una sorta di oggetto filtro. Ad esempio, gran parte del nostro sistema basato su Hadoop si basa su un Mapper che gestisce la serializzazione e la deserializzazione degli articoli su JSON e applica un numero di processori che prendono ciascuno un elemento di contenuto e lo modificano e lo restituiscono oppure restituiscono null rifiutare.


1

Nel documento Java di Var-Args è abbastanza chiaro l'uso di var args:

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

sull'uso dice:

"Quindi, quando dovresti usare varargs? Come client, dovresti approfittarne ogni volta che l'API li offre. Gli usi importanti nelle API principali includono la riflessione, la formattazione dei messaggi e la nuova funzione printf. Come progettista API, dovresti usarli con parsimonia, solo quando il vantaggio è davvero convincente. In generale, non dovresti sovraccaricare un metodo varargs, o sarà difficile per i programmatori capire quale sovraccarico viene chiamato. "

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.