Varargs in Java

1. Einleitung

Varargs wurden in Java 5 eingeführt und bieten eine Kurzform für Methoden, die eine beliebige Anzahl von Parametern eines Typs unterstützen.

In diesem Artikel werden wir sehen, wie wir diese Java-Kernfunktion verwenden können.

2. Vor Varargs

Vor Java 5 mussten wir, wenn wir eine beliebige Anzahl von Argumenten übergeben wollten, alle Argumente in einem Array übergeben oder N Methoden implementieren (eine für jeden zusätzlichen Parameter):

public String format() { ... } public String format(String value) { ... } public String format(String val1, String val2) { ... }

3. Verwendung von Varargs

Varargs helfen uns dabei, das Schreiben von Boilerplate-Code zu vermeiden, indem wir die neue Syntax einführen, die eine beliebige Anzahl von Parametern automatisch verarbeiten kann - unter Verwendung eines Arrays unter der Haube.

Wir können sie mithilfe einer Standardtypdeklaration definieren, gefolgt von einer Ellipse:

public String formatWithVarArgs(String... values) { // ... }

Und jetzt können wir unsere Methode mit einer beliebigen Anzahl von Argumenten aufrufen, wie zum Beispiel:

formatWithVarArgs(); formatWithVarArgs("a", "b", "c", "d");

Wie bereits erwähnt, sind Varargs Arrays, daher müssen wir mit ihnen genauso arbeiten wie mit einem normalen Array.

4. Regeln

Varargs sind einfach zu bedienen. Aber es gibt ein paar Regeln, die wir beachten müssen:

  • Jede Methode kann nur einen varargs- Parameter haben
  • Das Argument varargs muss der letzte Parameter sein

5. Haufenverschmutzung

Die Verwendung von Varargs kann zu einer sogenannten Heap-Verschmutzung führen. Betrachten Sie diese Varargs- Methode, um die Haufenverschmutzung besser zu verstehen :

static String firstOfFirst(List... strings) { List ints = Collections.singletonList(42); Object[] objects = strings; objects[0] = ints; // Heap pollution return strings[0].get(0); // ClassCastException }

Wenn wir diese seltsame Methode in einem Test aufrufen:

String one = firstOfFirst(Arrays.asList("one", "two"), Collections.emptyList()); assertEquals("one", one);

Wir würden eine ClassCastException erhalten , obwohl wir hier nicht einmal explizite Typumwandlungen verwendet haben:

java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String

5.1. Sichere Verwendung

Jedes Mal, wenn wir varargs verwenden , erstellt der Java-Compiler ein Array, das die angegebenen Parameter enthält. In diesem Fall erstellt der Compiler ein Array mit generischen Typkomponenten, die die Argumente enthalten.

Wenn wir Varargs mit generischen Typen verwenden, warnt uns der Java-Compiler vor einer möglichen unsicheren Verwendung von Varargs , da das Risiko einer schwerwiegenden Laufzeitausnahme besteht :

warning: [varargs] Possible heap pollution from parameterized vararg type T

Die Verwendung von varargs ist genau dann sicher, wenn:

  • Wir speichern nichts in dem implizit erstellten Array. In diesem Beispiel haben wir eine Liste in diesem Array gespeichert
  • Wir lassen keinen Verweis auf das generierte Array der Methode entgehen (dazu später mehr).

Wenn wir sicher sind, dass die Methode selbst die varargs sicher verwendet, können wir @SafeVarargs verwenden , um die Warnung zu unterdrücken.

Einfach ausgedrückt ist die Verwendung von varargs sicher, wenn wir damit eine variable Anzahl von Argumenten vom Aufrufer an die Methode übertragen und nicht mehr!

5.2. Escaping Varargs Referenz

Betrachten wir eine andere unsichere Verwendung von varargs :

static  T[] toArray(T... arguments) { return arguments; }

Auf den ersten Blick scheint die toArray- Methode völlig harmlos zu sein. Da das varargs-Array jedoch dem Aufrufer entkommen kann, verstößt es gegen die zweite Regel für sichere varargs .

Um zu sehen, wie gefährlich diese Methode sein kann, verwenden wir sie in einer anderen Methode:

static  T[] returnAsIs(T a, T b) { return toArray(a, b); }

Wenn wir dann diese Methode aufrufen:

String[] args = returnAsIs("One", "Two");

Wir würden wieder eine ClassCastException bekommen. Folgendes passiert, wenn wir die returnAsIs- Methode aufrufen :

  • Um a und b an die toArray- Methode zu übergeben, muss Java ein Array erstellen
  • Da das Objekt [] Elemente eines beliebigen Typs enthalten kann, erstellt der Compiler eines
  • Die toArray- Methode gibt das angegebene Object [] an den Aufrufer zurück
  • Da die Aufrufsite einen String [] erwartet , versucht der Compiler, das Objekt [] in den erwarteten String [] umzuwandeln , daher die ClassCastException

Für eine detailliertere Diskussion der Haufenverschmutzung wird dringend empfohlen, Punkt 32 von Effective Java von Joshua Bloch zu lesen.

6. Fazit

Varargs kann dazu führen, dass in Java viele Boilerplates verschwinden .

Und dank ihrer impliziten Autoboxing zu und von Array spielen sie eine Rolle bei der Zukunftssicherung unseres Codes.

Wie immer sind alle Codebeispiele aus diesem Artikel in unserem GitHub-Repository verfügbar.