Varargs in Java

1. Introduzione

I vararg sono stati introdotti in Java 5 e forniscono una scorciatoia per metodi che supportano un numero arbitrario di parametri di un tipo.

In questo articolo vedremo come possiamo utilizzare questa funzionalità principale di Java.

2. Prima di Varargs

Prima di Java 5, ogni volta che volevamo passare un numero arbitrario di argomenti, dovevamo passare tutti gli argomenti in un array o implementare N metodi (uno per ogni parametro aggiuntivo):

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

3. Uso di Varargs

I vararg ci aiutano a evitare di scrivere codice boilerplate introducendo la nuova sintassi in grado di gestire automaticamente un numero arbitrario di parametri, utilizzando un array sotto il cofano.

Possiamo definirli usando una dichiarazione di tipo standard, seguita da un'ellissi:

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

E ora, possiamo chiamare il nostro metodo con un numero arbitrario di argomenti, come:

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

Come accennato in precedenza, i vararg sono array quindi dobbiamo lavorare con loro proprio come lavoreremmo con un normale array.

4. Regole

I Vararg sono semplici da usare. Ma ci sono alcune regole che dobbiamo tenere a mente:

  • Ogni metodo può avere solo un parametro varargs
  • L' argomento varargs deve essere l'ultimo parametro

5. Inquinamento del mucchio

L'utilizzo di vararg può portare al cosiddetto Heap Pollution. Per comprendere meglio l'inquinamento dell'heap, considera questo metodo varargs :

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

Se chiamiamo questo strano metodo in un test:

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

Vorremmo ottenere un ClassCastException anche se non abbiamo nemmeno usare alcun tipo cast espliciti qui:

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

5.1. Utilizzo sicuro

Ogni volta che usiamo varargs , il compilatore Java crea un array per contenere i parametri dati. In questo caso, il compilatore crea un array con componenti di tipo generico per contenere gli argomenti.

Quando usiamo vararg con tipi generici, poiché esiste un potenziale rischio di un'eccezione fatale di runtime, il compilatore Java ci avverte di un possibile utilizzo non sicuro di vararg :

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

L' utilizzo di varargs è sicuro se e solo se:

  • Non memorizziamo nulla nell'array creato implicitamente. In questo esempio, abbiamo memorizzato un elenco in quell'array
  • Non lasciamo che un riferimento all'array generato sfugga al metodo (ne parleremo più avanti)

Se siamo sicuri che il metodo stesso utilizzi in modo sicuro i vararg, possiamo utilizzare @SafeVarargs per sopprimere l'avviso.

In parole povere, l' utilizzo di varargs è sicuro se li usiamo per trasferire un numero variabile di argomenti dal chiamante al metodo e niente di più!

5.2. Riferimento Varargs in fuga

Consideriamo un altro utilizzo non sicuro di varargs :

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

All'inizio, potrebbe sembrare che il metodo toArray sia completamente innocuo. Tuttavia, poiché consente all'array varargs di sfuggire al chiamante, viola la seconda regola dei vararg sicuri .

To see how this method can be dangerous, let's use it in another method:

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

Then if we call this method:

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

We would, again, get a ClassCastException. Here's what happens when we call the returnAsIs method:

  • To pass a and b to the toArray method, Java needs to create an array
  • Since the Object[] can hold items of any type, the compiler creates one
  • The toArray method returns the given Object[] to the caller
  • Since the call site expects a String[], the compiler tries to cast the Object[] to the expected String[], hence the ClassCastException

Per una discussione più dettagliata sull'inquinamento dell'heap, si consiglia vivamente di leggere l'articolo 32 di Effective Java di Joshua Bloch.

6. Conclusione

Varargs può far sparire molti boilerplate in Java.

E, grazie al loro implicito autoboxing da e verso Array, svolgono un ruolo nel rendere a prova di futuro il nostro codice.

Come sempre, tutti gli esempi di codice di questo articolo possono essere disponibili nel nostro repository GitHub.