Overloading dei Metodi in Java

Concetti Chiave
  • Il sovraccarico o overloading dei metodi in Java consente di definire più metodi con lo stesso nome ma con parametri diversi.
  • I metodi sovraccaricati devono differire nel tipo e/o nel numero dei loro parametri.
  • Il tipo di ritorno non è sufficiente per distinguere due versioni di un metodo sovraccaricato.
  • Java utilizza il tipo e/o il numero di argomenti per determinare quale versione del metodo sovraccaricato chiamare.
  • Il sovraccarico dei metodi supporta il polimorfismo, consentendo di utilizzare lo stesso nome per operazioni simili su tipi di dati diversi.

Overloading dei Metodi

In Java, è possibile definire due o più metodi all'interno della stessa classe che condividono lo stesso nome, purché le dichiarazioni dei loro parametri siano diverse. Quando ciò accade, si dice che i metodi sono sovraccaricati, e il processo è denominato sovraccarico di metodo o, in inglese, method overloading.

Il sovraccarico di metodo è uno dei modi con cui Java supporta il polimorfismo. Se non si è mai utilizzato un linguaggio che consente il sovraccarico dei metodi, il concetto può inizialmente sembrare strano. Ma, come si vedrà, il sovraccarico dei metodi è una delle funzionalità più interessanti e utili di Java.

Quando viene invocato un metodo sovraccaricato, Java utilizza il tipo e/o il numero di argomenti come guida per determinare quale versione del metodo sovraccaricato chiamare effettivamente.

Pertanto, i metodi sovraccaricati devono differire nel tipo e/o nel numero dei loro parametri. Sebbene i metodi sovraccaricati possano avere tipi di ritorno differenti, il solo tipo di ritorno non è sufficiente per distinguere due versioni di un metodo. Quando Java incontra una chiamata a un metodo sovraccaricato, esegue semplicemente la versione del metodo i cui parametri corrispondono agli argomenti usati nella chiamata.

Ecco un semplice esempio che illustra l'overloading dei metodi:

// Esempio di sovraccarico di metodo.
class DimostrazioneOverloading {
    void prova() {
        System.out.println("Nessun parametro");
    }

    // Overload di prova per un parametro intero.
    void prova(int a) {
        System.out.println("a: " + a);
    }

    // Overload di prova per due parametri interi.
    void prova(int a, int b) {
        System.out.println("a e b: " + a + " " + b);
    }

    // Overload di prova per un parametro double
    double prova(double a) {
        System.out.println("double a: " + a);
        return a * a;
    }
}

class Overloading {
    public static void main(String[] argomenti) {
        DimostrazioneOverloading oggetto = new DimostrazioneOverloading();
        double risultato;

        // chiamare tutte le versioni di prova()
        oggetto.prova();
        oggetto.prova(10);
        oggetto.prova(10, 20);
        risultato = oggetto.prova(123.25);
        System.out.println("Risultato di oggetto.prova(123.25): " + risultato);
    }
}

Questo programma genera il seguente output:

Nessun parametro
a: 10
a e b: 10 20
double a: 123.25
Risultato di oggetto.prova(123.25): 15190.5625

Come si può vedere, prova() è sovraccaricato quattro volte:

  1. La prima versione non prende parametri;
  2. la seconda prende un parametro intero;
  3. la terza prende due parametri interi;
  4. la quarta prende un parametro double.

Il fatto che la quarta versione di prova() restituisca un valore non ha alcuna conseguenza relativa al sovraccarico, poiché i tipi di ritorno non giocano alcun ruolo nella risoluzione dell'overloading dei metodi.

Risoluzione dell'Overloading dei Metodi

Quando viene chiamato un metodo sovraccaricato, Java cerca una corrispondenza tra gli argomenti usati per chiamare il metodo e i parametri del metodo stesso.

Tuttavia, questa corrispondenza non deve essere necessariamente esatta. In alcuni casi, le conversioni automatiche di tipo di Java possono avere un ruolo nella risoluzione del sovraccarico. Ad esempio, si consideri il seguente programma:

// Le conversioni automatiche di tipo si applicano all'Overloading
class DimostrazioneOverloading {
    void prova() {
        System.out.println("Nessun parametro");
    }

    // Overload di prova per due parametri interi.
    void prova(int a, int b) {
        System.out.println("a e b: " + a + " " + b);
    }

    // Overload di prova per un parametro double
    void prova(double a) {
        System.out.println("Dentro prova(double) a: " + a);
    }
}

class Overload {
    public static void main(String[] argomenti) {
        DimostrazioneOverloading oggetto = new DimostrazioneOverloading();
        int i = 88;

        oggetto.prova();
        oggetto.prova(10, 20);

        oggetto.prova(i); // questo invocherà prova(double)
        oggetto.prova(123.2); // questo invocherà prova(double)
    }
}

Questo programma genera il seguente output:

Nessun parametro
a e b: 10 20
Dentro prova(double) a: 88.0
Dentro prova(double) a: 123.2

Come si può vedere, questa versione di DimostrazioneOverloading non definisce prova(int).

Pertanto, quando prova() è chiamato con un argomento intero all'interno di Overload, non viene trovato alcun metodo corrispondente. Tuttavia, Java può convertire automaticamente un intero in un double, e questa conversione può essere utilizzata per risolvere la chiamata.

Pertanto, dopo che prova(int) non viene trovato, Java promuove i a double e poi chiama prova(double). Naturalmente, se prova(int) fosse stato definito, sarebbe stato chiamato invece. Java utilizzerà le sue conversioni automatiche di tipo solo se non viene trovata una corrispondenza esatta.

L'overload dei metodi supporta il polimorfismo perché è uno dei modi con cui Java implementa il paradigma una interfaccia, molti metodi.

Per comprendere come, si consideri quanto segue. Nei linguaggi che non supportano l'overloading (ad esempio il linguaggio C), ogni metodo deve avere un nome univoco. Tuttavia, spesso si desidera implementare essenzialmente lo stesso metodo per diversi tipi di dati. Si consideri la funzione del valore assoluto.

Nei linguaggi che non supportano il sovraccarico, ci sono di solito tre o più versioni di questa funzione, ognuna con un nome leggermente diverso. Ad esempio, in C, la funzione abs() restituisce il valore assoluto di un intero, labs() restituisce il valore assoluto di un intero long, e fabs() restituisce il valore assoluto di un numero a virgola mobile. Poiché C non supporta il sovraccarico, ciascuna funzione ha il proprio nome, anche se tutte e tre le funzioni fanno essenzialmente la stessa cosa.

Ciò rende la situazione più complessa, concettualmente, di quanto sia in realtà. Anche se la logica di ciascuna funzione è la stessa, si devono comunque ricordare tre nomi distinti. Questo problema non si verifica in Java, perché ciascuna di queste funzioni del valore assoluto può utilizzare lo stesso nome. Infatti, la libreria standard di classi di Java include un metodo del valore assoluto sovraccarico, chiamato abs(). Questo metodo è sovraccaricato nella classe Math di Java per gestire tutti i tipi numerici. Java determina quale versione di abs() chiamare in base al tipo dell'argomento utilizzato.

Spetta al compilatore scegliere la versione specifica corretta per una determinata circostanza.

Il programmatore deve solo ricordare l'operazione generale che viene eseguita. Attraverso l'applicazione del polimorfismo, diversi nomi sono stati ridotti a uno solo.

Sebbene questo esempio sia piuttosto semplice, espandendo il concetto si può comprendere come il sovraccarico possa aiutare a gestire una maggiore complessità.

Quando si sovraccarica un metodo, ciascuna versione di quel metodo può eseguire qualsiasi operazione desiderata.

Non esiste alcuna regola che stabilisca che i metodi sovraccarichi debbano essere correlati tra loro. Tuttavia, da un punto di vista stilistico, il sovraccarico di metodi implica una relazione.

Pertanto, sebbene sia possibile utilizzare lo stesso nome per sovraccaricare metodi non correlati, non è consigliabile farlo.

Ad esempio, si potrebbe usare il nome quadrato per creare metodi che restituiscono il quadrato di un intero e la radice quadrata di un numero in virgola mobile.

Ma queste due operazioni sono fondamentalmente diverse. Applicare il sovraccarico di metodi in questo modo ne vanifica lo scopo originale. In pratica, è consigliabile sovraccaricare solo operazioni strettamente correlate.