Menu

Overloading dei metodi in Java: stesso nome, parametri diversi

Come l'overloading dei metodi in Java permette a più metodi di condividere un nome con parametri diversi, come il compilatore sceglie la versione giusta e quali ambiguità evitare.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Un nome, più versioni

Nella pagina precedente hai visto come i parametri di un metodo definiscono ciò che accetta. L'overloading dei metodi va oltre: puoi dare lo stesso nome a più metodi, purché le loro liste di parametri siano diverse. Il compilatore li tratta come metodi distinti e sceglie quello giusto in base agli argomenti che passi.

È per questo che System.out.println stampa senza problemi un int, una String, un boolean o un double: non esiste un solo println, ci sono molti overload che condividono il nome. Tu scrivi la chiamata che intendi e il compilatore la associa alla versione adatta.

Entrambi i metodi si chiamano square, ma uno accetta un int e l'altro un double. Il letterale 5 è un int, quindi viene eseguito il primo overload; 2.5 è un double, quindi viene eseguito il secondo.

Cosa rende diverso un overload

Gli overload devono differire nella lista dei parametri, cioè in almeno uno di questi aspetti:

  • un numero diverso di parametri,
  • tipi di parametri diversi, oppure
  • un ordine diverso dei tipi.

Ogni chiamata ha una lista di parametri che corrisponde esattamente a uno dei tre metodi join, quindi non c'è confusione.

Il tipo di ritorno non conta

Una trappola comune per chi inizia: provare a fare overloading solo sul tipo di ritorno. Il tipo di ritorno non fa parte della firma usata dal compilatore, quindi questo codice non compila:

// NON compila: stesso nome, stessi parametri, cambia solo il tipo di ritorno
static int   value() { return 1; }
static double value() { return 1.0; }   // errore: value() è già definito

Il compilatore non riesce a distinguerli, perché in una chiamata come value() niente negli argomenti suggerisce quale vuoi. Puoi dare agli overload tipi di ritorno diversi, ma solo quando le loro liste di parametri sono già diverse.

Come Java sceglie un overload

Quando più di un overload potrebbe accettare i tuoi argomenti, Java sceglie quello più specifico e preferisce una corrispondenza esatta del tipo a una conversione di allargamento. Guarda cosa succede con un argomento int:

show(7) corrisponde esattamente a int, anche se long e double potrebbero contenere 7 dopo un allargamento. Solo se l'overload esatto venisse rimosso il compilatore allargherebbe int a long, poi a double. Questa risoluzione viene decisa interamente in fase di compilazione, in base ai tipi dichiarati degli argomenti.

Attenzione alle chiamate ambigue

Se nessun overload è chiaramente il migliore, il compilatore si rifiuta di indovinare e segnala un errore. Succede soprattutto con null, che va bene per qualsiasi tipo riferimento:

static void handle(String s) { }
static void handle(StringBuilder b) { }

handle(null);   // errore: il riferimento a handle è ambiguo

Entrambi gli overload accettano null e nessuno dei due è più specifico, quindi la chiamata non compila. Risolvi rendendo esplicito il tipo con un cast, handle((String) null), oppure riprogettando gli overload in modo che non entrino in conflitto. La stessa attenzione vale quando mescoli autoboxing e allargamento: tieni gli insiemi di overload abbastanza semplici da avere un solo vincitore ovvio per ogni chiamata.

Overloading dei costruttori

L'overloading non riguarda solo i metodi normali: i costruttori lo usano di continuo per offrire più modi di creare un oggetto. Un costruttore senza argomenti può passare il lavoro a uno più completo con this(...):

I due costruttori condividono il nome Point ma differiscono nel numero di parametri, esattamente come i metodi in overloading. Delegare con this(...) tiene la logica di inizializzazione in un solo punto.

Overloading e overriding

Questi due termini si somigliano, ma non hanno niente a che fare l'uno con l'altro:

  • Overloading: stesso nome, liste di parametri diverse, nella stessa classe. Il compilatore sceglie la versione in fase di compilazione. Serve a offrire varianti di un'operazione.
  • Overriding: una sottoclasse ridefinisce un metodo ereditato con lo stesso nome e gli stessi parametri. Java sceglie la versione a runtime in base al tipo reale dell'oggetto. Serve a sostituire un comportamento (lo incontrerai con l'ereditarietà e il polimorfismo).

Se le liste di parametri sono identiche, stai facendo overriding (o stai causando un errore di metodo duplicato nella stessa classe); se sono diverse, stai facendo overloading.

Prossima pagina: varargs

L'overloading ti permette di scrivere join(a, b) e join(a, b, c) come metodi separati, ma cosa succede se vuoi accettare un numero qualsiasi di argomenti senza dichiarare un overload per ogni quantità? La sintassi varargs di Java permette a un solo metodo di ricevere una lista di argomenti di lunghezza variabile, ed è l'argomento della prossima pagina.

Domande frequenti

Cos'è l'overloading dei metodi in Java?

Overloading significa definire più metodi con lo stesso nome nella stessa classe, ognuno con una lista di parametri diversa (un numero diverso di parametri, tipi diversi o un ordine diverso dei tipi). Il compilatore decide quale versione chiamare confrontando gli argomenti che passi con i parametri di ciascun overload. È una decisione presa in fase di compilazione, non a runtime.

Due metodi possono differire solo per il tipo di ritorno in Java?

No. Il tipo di ritorno non fa parte della firma di un metodo ai fini dell'overloading, quindi int total() e double total() nella stessa classe sono un errore di compilazione. Gli overload devono differire nella lista dei parametri: numero, tipi od ordine. Il tipo di ritorno può essere diverso, ma solo in aggiunta a una differenza nei parametri, non da solo.

Qual è la differenza tra overloading e overriding in Java?

L'overloading è avere più metodi con lo stesso nome ma parametri diversi nella stessa classe, e lo risolve il compilatore in fase di compilazione. L'overriding è una sottoclasse che ridefinisce un metodo ereditato con lo stesso nome e gli stessi parametri, e si risolve a runtime in base al tipo reale dell'oggetto. L'overloading serve a offrire varianti; l'overriding serve a sostituire un comportamento.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA