Wann verwenden Sie varargs in Java?

192

Ich habe Angst vor Varargs. Ich weiß nicht, wofür ich sie verwenden soll.

Außerdem ist es gefährlich, die Leute so viele Argumente führen zu lassen, wie sie wollen.

Was ist ein Beispiel für einen Kontext, in dem sie gut verwendet werden können?

Harry Quince
quelle
3
Ich verstehe nicht, warum es "gefährlich" wäre. Es ist nicht gefährlicher, als eine Methode zu haben, die viele Male mit unterschiedlichen Argumenten aufgerufen wird. Haben Sie Bedenken hinsichtlich des Stapels? Dann sollten Sie dies nicht tun, da Varargs Arrays zugeordnet werden, die als Referenz übergeben werden.
jfpoilpret
66
Ich wollte mich nur einschalten: Es ist nichts Falsches daran, Sprachfunktionen zu vermeiden, mit denen Sie (noch) nicht ganz vertraut sind. Weitaus besser als Funktionen zu verwenden, die Sie nicht verstehen! ;)
Steven
2
Es ist normal, Angst vor Unbekanntem zu haben. Weitere Informationen zu Varargs finden Sie hier docs.oracle.com/javase/tutorial/java/javaOO/arguments.html . Sie werden sehen, dass Varargs nichts zu befürchten sind. Varargs sind nützlich. Wenn Sie wissen, wie man Varargs verwendet, können Sie Methoden wie [PrintStream.format] schreiben (" docs.oracle.com/javase/7/docs/api/java/io/… , java.lang.Object ...)" ) :).
Entwickler Marius Žilėnas
1
Dies ist keine Kritik an Varargs, aber es gibt tatsächlich einen Grund, "Angst zu haben" (bis Sie die Grenzen von Varargs genau verstehen); Aus diesem Grund ist die Annotation @SafeVarargs vorhanden. stackoverflow.com/a/14252221/1593924
Jon Coombs

Antworten:

150

Varargs sind nützlich für jede Methode, die mit einer unbestimmten Anzahl von Objekten umgehen muss . Ein gutes Beispiel ist String.format. Die Formatzeichenfolge kann eine beliebige Anzahl von Parametern akzeptieren, sodass Sie einen Mechanismus benötigen, um eine beliebige Anzahl von Objekten zu übergeben.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);
Andy White
quelle
5
Ein Array-Parameter kann auch eine unbestimmte Anzahl von Objekten empfangen, aber ein varargs-Parameter ermöglicht mehr Flexibilität und Bequemlichkeit am Aufrufpunkt. Sie können Code schreiben, um ein Array zu erstellen und zu übergeben, oder Sie können Java dies für Sie tun lassen, wenn Sie ihn in einem varargs-Parameter empfangen möchten.
H2ONaCl
79

Eine gute Faustregel wäre:

"Verwenden Sie varargs für jede Methode (oder jeden Konstruktor), die ein Array von T (unabhängig vom Typ T) als Eingabe benötigt."

Dies erleichtert das Aufrufen dieser Methoden (dies ist nicht erforderlich) new T[]{...} ).

Sie können diese Regel erweitern, um Methoden mit einem List<T>Argument einzuschließen, vorausgesetzt, dieses Argument dient nur zur Eingabe (dh die Liste wird von der Methode nicht geändert).

Außerdem würde ich auf die Verwendung verzichten f(Object... args) da dies zu einer Programmiermethode mit unklaren APIs führt.

In Bezug auf Beispiele habe ich es in DesignGridLayout verwendet , wo ich mehrere JComponents in einem Aufruf hinzufügen kann :

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

Im obigen Code ist die add () -Methode definiert als add(JComponent... components) .

Schließlich muss bei der Implementierung solcher Methoden berücksichtigt werden, dass sie mit einem leeren vararg aufgerufen werden können! Wenn Sie mindestens ein Argument vorbringen möchten, müssen Sie einen hässlichen Trick anwenden, wie z.

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

Ich halte diesen Trick für hässlich, da die Implementierung der Methode weniger einfach ist als nur T... argsin der Argumentliste.

Hoffentlich hilft dies dabei, den Punkt über Varargs zu klären.

jfpoilpret
quelle
1
Haben Sie darüber nachgedacht, if (args.length == 0) throw new RuntimeException("foo");stattdessen eine Vorbedingungsprüfung hinzuzufügen ? (Da der Anrufer gegen den Vertrag verstieß)
Micha Wiedenmann
24
Nun, der Zweck einer guten API ist es, Missbrauch so früh wie möglich zu verhindern, also zum Zeitpunkt der Kompilierung, wenn es möglich ist. Daher stellt der Vorschlag dafür void f(T arg1, T... args)immer sicher, dass sie niemals ohne Argument aufgerufen wird, ohne bis zur Laufzeit warten zu müssen.
jfpoilpret
Ich denke, dass die meiste Zeit ein Aufruf einer Nur-Varargs-Funktion ohne Argumente nur dazu führt, dass überhaupt nichts getan wird. Der Punkt ist, dass die Bereitstellung eines Arguments für eine Varargs-Funktion höchstwahrscheinlich keinen erheblichen Schaden anrichten wird.
WorldSEnder
Varargs sind nützlich, aber sie entsprechen nicht der Verwendung eines Arrays. Varargs sind nicht reifizierbar. Daher sind sie wie Generika von der Typlöschung betroffen, die je nach Auftrag eine inakzeptable Einschränkung darstellen kann.
Julian
34

Ich verwende Varargs häufig für die Ausgabe in die Protokolle zum Zwecke des Debuggens.

So ziemlich jede Klasse in meiner App hat eine Methode debugPrint ():

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

Dann habe ich innerhalb der Methoden der Klasse folgende Aufrufe:

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

Wenn ich zufrieden bin, dass mein Code funktioniert, kommentiere ich den Code in der debugPrint () -Methode aus, damit die Protokolle nicht zu viele irrelevante und unerwünschte Informationen enthalten, aber ich kann die einzelnen Aufrufe von debugPrint () unkommentiert lassen. Wenn ich später einen Fehler finde, kommentiere ich einfach den debugPrint () - Code aus und alle meine Aufrufe von debugPrint () werden reaktiviert.

Natürlich könnte ich genauso gut auf Varargs verzichten und stattdessen Folgendes tun:

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

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

In diesem Fall muss der Server jedoch beim Auskommentieren des debugPrint () - Codes immer noch alle Variablen in jedem Aufruf von debugPrint () verketten, obwohl mit der resultierenden Zeichenfolge nichts unternommen wird. Wenn ich jedoch varargs verwende, muss der Server sie nur in ein Array einfügen, bevor er feststellt, dass er sie nicht benötigt. Es wird viel Zeit gespart.

Bob
quelle
4
Sie können eine Debug-Methode in der Oberklasse implementieren, um jedes Objekt mithilfe von Reflection zu drucken.
Lluis Martinez
12

Varargs können verwendet werden, wenn wir uns nicht sicher sind, wie viele Argumente in einer Methode übergeben werden sollen. Es erstellt ein Array von Parametern mit nicht angegebener Länge im Hintergrund, und ein solcher Parameter kann zur Laufzeit als Array behandelt werden.

Wenn wir eine Methode haben, die überladen ist, um eine unterschiedliche Anzahl von Parametern zu akzeptieren, können wir anstelle einer unterschiedlichen Überladung der Methode einfach das varargs-Konzept verwenden.

Auch wenn der Typ der Parameter variieren wird, vereinfacht die Verwendung von "Object ... test" den Code erheblich.

Beispielsweise:

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

Hier wird indirekt ein Array vom Typ int (Liste) als Parameter übergeben und im Code als Array behandelt.

Zum besseren Verständnis folgen Sie diesem Link (es hat mir sehr geholfen, dieses Konzept klar zu verstehen): http://www.javadb.com/using-varargs-in-java

PS: Sogar ich hatte Angst, Varargs zu verwenden, wenn ich es nicht wusste. Aber jetzt bin ich daran gewöhnt. Wie es heißt: "Wir klammern uns an das Bekannte, aus Angst vor dem Unbekannten", also benutze es einfach so oft du kannst und auch du wirst es mögen :)

Sarvan
quelle
4
Das C # -Äquivalent von varargs ist "params". Es macht auch das Gleiche und akzeptiert eine variable Anzahl von Parametern. Sehen Sie dies zum besseren Verständnis: dotnetperls.com/params
Sarvan
11

Varargs ist die Funktion, die in Java Version 1.5 hinzugefügt wurde.

Warum das benutzen?

  1. Was ist, wenn Sie nicht wissen, wie viele Argumente für eine Methode übergeben werden müssen?
  2. Was ist, wenn Sie einer Methode eine unbegrenzte Anzahl von Argumenten übergeben möchten?

Wie funktioniert das?

Es erstellt ein Array mit den angegebenen Argumenten und übergibt das Array an die Methode.

Beispiel:

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

}

Ausgabe :

2

Summe ist 12

3

Summe ist 21

Arun Prakash
quelle
6

Ich habe auch eine Varargs-bezogene Angst:

Wenn der Aufrufer ein explizites Array an die Methode übergibt (im Gegensatz zu mehreren Parametern), erhalten Sie einen gemeinsamen Verweis auf dieses Array.

Wenn Sie dieses Array intern speichern müssen, möchten Sie es möglicherweise zuerst klonen, um zu verhindern, dass der Anrufer es später ändern kann.

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

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

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

Dies unterscheidet sich zwar nicht wirklich von der Übergabe eines Objekts, dessen Status sich später ändern könnte, da das Array normalerweise (bei einem Aufruf mit mehreren Argumenten anstelle eines Arrays) ein neues ist, das vom Compiler intern erstellt wurde und das Sie sicher erstellen können Verwenden Sie, dies ist sicherlich unerwartetes Verhalten.

Thilo
quelle
1
Richtig, aber dieses Beispiel scheint etwas weit hergeholt. Ist es wahrscheinlich, dass Leute Ihre API auf diese Weise verwenden?
jfpoilpret
2
Man weiß es nie ... Und besonders wenn die Anzahl der Argumente auf der aufrufenden Seite nicht fest codiert, sondern auch in einer Liste in einer Schleife zusammengefasst ist, ist die Übergabe eines Arrays keine Seltenheit.
Thilo
5
Ich denke, Ihr Anwendungsfallbeispiel ist im Allgemeinen nicht unwahrscheinlich. Man könnte argumentieren, dass Ihre API das Eingabearray nicht auf diese Weise verwenden sollte, und wenn dies der Fall ist, muss sie es dokumentieren.
Lawrence Dol
Überladen Sie die Methode, um beide zu unterstützen. argMethod (Object .. objList) argMethod (Object [] objList)
Toolman
2
@thetoolman: Ich denke nicht, dass das möglich ist. Es wird als doppelte Methode angesehen.
Thilo
1

Ich verwende varargs häufig für Konstruktoren, die eine Art Filterobjekt aufnehmen können. Beispielsweise basiert ein großer Teil unseres auf Hadoop basierenden Systems auf einem Mapper, der die Serialisierung und Deserialisierung von Elementen in JSON verwaltet und eine Reihe von Prozessoren anwendet, die jeweils ein Inhaltselement übernehmen und es entweder ändern und zurückgeben oder null zurückgeben ablehnen.

Kevin Peterson
quelle
1

In Java doc von Var-Args ist die Verwendung von var args ziemlich klar:

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

Über die Nutzung heißt es:

"Wann sollten Sie also varargs verwenden? Als Client sollten Sie sie nutzen, wann immer die API sie anbietet. Wichtige Verwendungszwecke in Kern-APIs sind Reflexion, Nachrichtenformatierung und die neue printf-Funktion. Als API-Designer sollten Sie sie verwenden sparsam, nur wenn der Nutzen wirklich überzeugend ist. Im Allgemeinen sollten Sie eine varargs-Methode nicht überladen, da es sonst für Programmierer schwierig ist, herauszufinden, welche Überladung aufgerufen wird. "

Surendra
quelle