Un metodo, un numero qualsiasi di argomenti
A volte non sai in anticipo di quanti argomenti ha bisogno un metodo. Potresti volere max(3, 9) in un momento e max(1, 7, 4, 2, 8) subito dopo. Senza varargs scriveresti un overload per ogni quantità oppure obbligheresti chi chiama a costruire un array. I varargs, abbreviazione di variable arguments (argomenti variabili), permettono a un solo metodo di accettare zero, uno o tanti valori dello stesso tipo.
Li dichiari mettendo tre puntini (...) dopo il tipo del parametro:
Chi chiama passa valori sciolti; dentro il metodo numbers è un vero array. Tutta l'idea è questa: il compilatore raccoglie per te gli argomenti in un array.
I varargs sono solo un array
A runtime non c'è nessuna magia. Un parametro dichiarato int... numbers è esattamente un int[] una volta dentro il metodo. Puoi chiederne la length, accedere agli indici e scorrerlo con un ciclo come qualsiasi altro array.
Proprio perché è davvero un array, puoi anche passare a un metodo varargs un array che hai già: accetta entrambe le forme:
Le regole: ultimo e unico
Due regole evitano ambiguità con i varargs, e il compilatore le fa rispettare entrambe:
- Il parametro varargs deve essere l'ultimo. Prima vengono i parametri normali, poi il parametro
...raccoglie tutto il resto. - Un metodo può avere al massimo un parametro varargs. Con due sarebbe impossibile capire dove finisce uno e dove inizia l'altro.
Questo compila. Ma mettere i varargs per primi, static void log(Object... values, String level), è un errore di compilazione: varargs parameter must be the last. Il parametro obbligatorio level viene sempre prima del ....
Dove usi già i varargs
Hai sempre chiamato metodi varargs. System.out.printf e String.format sono gli esempi classici: il loro secondo parametro è Object... args:
Anche List.of(...), Arrays.asList(...) e molte API in stile builder usano i varargs. Ecco perché puoi passare loro un numero qualsiasi di elementi senza incapsulare niente in un array.
Varargs e overloading
Varargs e overloading interagiscono in un modo sorprendente. Quando sia un overload normale sia un overload varargs potrebbero corrispondere a una chiamata, Java preferisce quello più specifico: vince il metodo con un numero fisso di parametri:
pick(1, 2) corrisponde a entrambi, ma Java sceglie l'overload senza varargs perché è quello più adatto. Il metodo varargs viene eseguito solo quando nessun overload fisso corrisponde. Di solito è ciò che vuoi, ma significa che aggiungere un overload varargs può cambiare in silenzio il metodo scelto da una chiamata.
Attenzione alle chiamate vuote e a null
Due trappole colpiscono spesso. La prima: chiamare un metodo varargs senza argomenti gli passa un array vuoto, non null, quindi numbers.length vale 0 e il ciclo non fa niente. Non ti serve un controllo su null per il caso senza argomenti:
La seconda: passare un null letterale è ambiguo e pericoloso. Java può interpretare count(null) come "l'intero array varargs è null" invece che "un elemento null", e questo lancia una NullPointerException quando leggi la sua length. Se intendi davvero un singolo elemento null, sii esplicito con count((Object) null).
Prossima pagina: classi
I varargs completano ciò che puoi fare con i parametri dei metodi: liste di argomenti flessibili in aggiunta all'overloading. Finora i metodi sono rimasti sparsi in una classe con static. Ora vedrai come funzionano davvero le classi: raggruppare dati (campi) e comportamento (metodi) in tipi tutti tuoi, il cuore della programmazione a oggetti in Java.
Domande frequenti
Cosa sono i varargs in Java?
I varargs (argomenti variabili) permettono a un metodo di accettare un numero qualsiasi di argomenti dello stesso tipo. Li dichiari mettendo ... dopo il tipo: int sum(int... numbers). Chi chiama può passare zero, uno o tanti valori, e dentro il metodo numbers è un normale array. È zucchero sintattico: Java raccoglie per te gli argomenti sciolti in un array.
Un metodo Java può avere più di un parametro varargs?
No. Un metodo può avere al massimo un parametro varargs, e deve essere l'ultimo della lista dei parametri, per esempio void log(String level, Object... values). Se non fosse l'ultimo, Java non saprebbe dove finisce la parte di lunghezza variabile e dove inizia il parametro successivo. I parametri normali vengono sempre prima del parametro ....
Che differenza c'è tra varargs e un parametro array in Java?
A runtime sono quasi uguali: dentro il metodo un parametro varargs int... è un int[]. La differenza sta nella chiamata: con i varargs scrivi sum(1, 2, 3) (valori sciolti), mentre con un parametro array esplicito devi costruire e passare un array, sum(new int[]{1, 2, 3}). Un metodo varargs accetta anche direttamente un array, quindi è la scelta più flessibile.