funzioni

parti di codice riutilizzabili che svolgono compiti specifici

Partiamo subito da un caso specifico: poniamo di voler costruire un programma per calcolare la media o il massimo di un elenco di valori contenuti in una casella di testo, il risultato sarebbe un programma come quello qui sotto.

public class MediaMaggiorePlain extends Application{

    TextField valori = new TextField();
    Label risposta = new Label();
    
    @Override
    void start(Stage finestra){
        Label e_elenco = new Label("elenco: ");
        Label e_risposta = new Label("risposta: ");
        Button p_media = new Button("media");
        Button p_massimo = new Button("massimo");
        
        GridPane griglia = new GridPane();
        griglia.add(e_elenco, 0, 0);
        griglia.add(valori, 1, 0, 2, 1);
        griglia.add(p_media, 1, 1);
        griglia.add(p_massimo, 2, 1);
        griglia.add(e_risposta, 0, 2);
        griglia.add(risposta, 1, 2);
        
        Scene scena = new Scene(griglia);
        finestra.setScene(scena);
        finestra.setTitle("media & massimo");
        finestra.show();
        
        p_media.setOnAction(evento->calcolaMedia());
        p_massimo.setOnAction(evento->trovaMassimo());
    }
    
    void trovaMassimo(){
        String testi[] = valori.getText().split(" ");
        int numeri[] = new int[testi.length];
        int massimo;
        for (int i = 0; i < testi.length; i++) {
            numeri[i] = Integer.parseInt(testi[i]);
        }
        
        massimo = numeri[0];
        for (int i = 1; i < numeri.length; i++) {
            if(numeri[i]>massimo){
                massimo = numeri[i];
            }
        }
        risposta.setText(""+massimo);
    }
    
    void calcolaMedia(){
        String testi[] = valori.getText().split(" ");
        int numeri[] = new int[testi.length];
        int somma=0,media;
        for (int i = 0; i < testi.length; i++) {
            numeri[i] = Integer.parseInt(testi[i]);
        }
        
        for (int i = 0; i < numeri.length; i++) {
            somma += numeri[i];
        }
        media = somma/numeri.length;
        risposta.setText(""+media);
    }

    public static void main(String args[]){
        launch(args);
    }   
}

Una parte del codice è evidenziata per far vedere che ci sono due segmenti che si ripetono praticamente identici, questo fatto comporta una serie di problemi:

Fortunatamente in tutti i linguaggi attuali c'è una soluzione: scrivere una funzione (che nei linguaggi ad oggetti si chiama "metodo") che fa il lavoro comune. Questa funzione farà un lavoro ben determinato: leggerà i dati dalla casella valori e avrà come risultato un vettore di interi, in pratica:

int[] leggiDati(){
    String testi[] = valori.getText().split(" ");
    int numeri[] = new int[testi.length];
    for (int i = 0; i < testi.length; i++) {
        numeri[i] = Integer.parseInt(testi[i]);
    }
    return numeri;
}

Proviamo a vedere cosa c'è scritto nel pezzo di codice precedente. Dalla prima riga, che è la dichiarazione della funzione, si possono ricavare alcune informazioni: stiamo dichiarando una funzione (un tipo seguito da un nome e da una parentesi tonda), la funzione si chiama "leggiDati" (il nome prima della tonda aperta) e da come risultato un vetore di interi (il tipo prima del nome).

Tutto il resto dovrebbe risultare familiare tranne l'ultima riga: return è una istruzione che indica cosa va restituito a chi chiama (sarebbe a dire usa) la funzione.

Usare una funzione non è molto difficile, è esattamente come usare il metodo getText() di un Button, niente di più, quel che cambia è che possiamo direttamente scrivere il nome della funzione perché si trova nella nostra classe (non serve scrivere il nome di un oggetto seguito da ".")

void trovaMassimo(){
    int numeri[] = leggiDati();
    int massimo;
       
    massimo = numeri[0];
    for (int i = 1; i < numeri.length; i++) {
        if(numeri[i]>massimo){
            massimo = numeri[i];
        }
    }
    risposta.setText(""+massimo);
}

Passaggio di parametri

Rispetto all'esempio precedente potrebbe succedere che l'elenco dei numeri si trovi in una casella oppure in un'altra, in questo caso la nostra funzione non va più bene perché legge sempre dalla stessa casella: potrebbe in questo caso far comodo modificare la nostra funzione in modo che prenda come input un testo (String) e restituisca un vettore di numeri (esattamente come prima).

int[] daStringaAVettore(String testo){ 
    String testi[] = testo.split(" ");
    int numeri[] = new int[testi.length];
    for (int i = 0; i < testi.length; i++) {
        numeri[i] = Integer.parseInt(testi[i]);
    }
    return numeri;
}

In questo caso per usare la funzione sarà necessario fare un passaggio in più perché la funzione vuole un dato in input (un testo) quindi leggerò un testo e lo fornirò come input (in realtà si dice che "lo passerò come parametro"):

String testoCasella = valori.getText();
int numeri[] = daStringaAVettore(testoCasella);

Adesso possiamo riassumere: una dichiarazione di funzione è un tipo seguito da un nome (stesse regole dei nomi di variabili) e da una lista di parametri tra parentesi tonde (anche se non ce ne è nessuno va bene) e un blocco di codice racchiuso tra graffe, in pratica:

daStringaAVettore
il nome della funzione (viene usato ad esempio per chiamarla)
int[]
(la parte prima del nome) la funzione restituirà (a chi l'ha chiamata) un vettore di interi
String testo
la funzione richiede un parametro (si chiamano parametri formali) di tipo String che all'interno della funzione sarà riferito come testo (cioè all'interno della funzione potremmo usare una variabile chiamata testo

Riguardo public int calcolaNumero(int a, int b) quali delle seguenti affermazioni sono vere?

restituisce una stringa ha due parametri ha un parametro intero si chiama calcolaNumero

Come funziona

Usiamo l'esempio qui sotto per definire con precisione come funzionano le cose: il programma qui sotto inizierà la sua esecuzione dalla fuzione chiamata main (alla riga 7).

La riga uno contiene la dichiarazione della funzione: il suo nome è somma, restituirà un numero intero e ha due parametri formali: n e q che assumeranno un valore quando la funzione verrà chiamata (cioè utilizzata). La funzione potrebbe non ritornare nessun valore e in quel caso il tipo ritornato sarebbe void.

Le righe 2 e 3 servono a calcolare la somma mentre la 4 è quella che dice che la funzione deve terminare e restituire a chi l'ha chiamata (ad esempio alla riga 9) il valore contenuto nella variabile s. return è seguita da una espressione (anche una semplice variabile è una espressione) che deve essere dello stesso tipo ritornato dalla funzione (nel caso di funzioni void non avrà alcuna espressione), quando si esegue un return la funzione termina.

Il programma inizia la sua esecuzione alla riga 7, la 8 è una semplice dichiarazione di variabile mentre la 9 è quella che ci interessa: quando il programma arriva qui sospende la funzione main e passa a somma copiando i parametri attuali "3" e "9" in quelli formali "n" e "q" della funzione. A questo punto viene eseguita la riga 3, s = "3 + 9" e poi alla 4 la funzione termina restituendo 12.

Quindi l'esecuzione torna alla riga 10 dove avevamo sospeso, il valore restituito dalla funzione somma viene copiato nella variabile a che quindi a questo punto conterrà 12.

avvia programma
pausa

funzioni e variabili

codice

 1int somma(int n, int q){
 2  int s;
 3  s = n + q;
 4  return s;
 5}
 6  
 7void main()
 8  int a;
 9  int b;
10  a = somma(3, 9);
11  b = a*2;
12}

Passaggio per valore

In Java il passaggio dei parametri (dalla funzione che chiama a quella che viene chiamata) avviene per valore, questo vuol dire che nel parametro formale viene copiato il valore passato come parametro attuale. Sembra complicato? Vediamo con un esempio.

public class Incrementa {
    static int incrementa(int x){ 
        x++;
        return x;
    }
    public static void main(String[] args) {
        int a = 10;
        int b = incrementa(a);
        System.out.println(a+", "+b);
    }
}

cosa comparirà come risultato del programma sopra?

11, 11no, la variabile a non viene modificata dalla funzione perché x contiene una copia del valore di a, non è la stessa variabile 10, 11 giusto, x viene incrementata ma è una copia di a che quindi non viene mai modificata nel programma