CodeGym /Corsi /JAVA 25 SELF /Passaggio dei parametri per valore e per riferimento

Passaggio dei parametri per valore e per riferimento

JAVA 25 SELF
Livello 8, Lezione 4
Disponibile

1. Introduzione

In diversi linguaggi di programmazione esistono due modi principali di passare i parametri alle funzioni (o ai metodi):

  • Per valore: la funzione riceve una copia del valore della variabile. Se la funzione modifica il parametro, l'originale non cambia.
  • Per riferimento: la funzione riceve un «riferimento» (puntatore) all'oggetto originale. Le modifiche all'interno della funzione si riflettono sull'originale.

In Java TUTTI i parametri sono passati per valore!
Ma c'è una sottigliezza: quando passiamo i tipi primitivi (int, double, boolean ecc.), si copia il valore stesso. Quando invece passiamo i tipi di riferimento (per esempio array o oggetti), si copia il valore del riferimento (cioè l'«indirizzo» dell'oggetto in memoria), non l'oggetto stesso.

Se vi sembra che il cervello inizi a fondersi, niente panico — ora mettiamo tutto in ordine e lo mostriamo con esempi!

2. Tipi primitivi: si copia il valore

Cominciamo dal semplice: cosa succede se proviamo a modificare il valore di una variabile di tipo int all'interno di un metodo?

public class Demo 
{
    public static void main(String[] args) 
    {
        int number = 5;
        changeValue(number);
        System.out.println(number); // Cosa stamperà?
    }

    public static void changeValue(int n) 
    {
        n = 42;
    }
}

Risultato: sullo schermo verrà stampato 5.

Perché?
Al metodo changeValue viene passata una COPIA del valore della variabile number. All'interno del metodo modifichiamo solo la copia (n); la variabile originale number in main rimane intatta.

Analogia
Immaginate di aver dato a un amico la fotocopia del passaporto, non l'originale. L'amico può disegnare baffi sulla copia quanto vuole — il vostro passaporto vero rimarrà invariato!

3. Tipi di riferimento: si copia il riferimento

Ora proviamo la stessa cosa con un array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        changeFirst(numbers);
        System.out.println(numbers[0]); // Cosa stamperà?
    }

    public static void changeFirst(int[] arr) 
    {
        arr[0] = 99;
    }
}

Risultato: sullo schermo verrà stampato 99.

Perché?
Al metodo changeFirst viene passata una copia del RIFERIMENTO all'array numbers. Entrambi i nomi (numbers in main e arr nel metodo) puntano allo stesso array in memoria. Perciò, se modifichiamo un elemento dell'array dentro il metodo, la modifica è «visibile» dall'esterno.

Visualizzazione

main: numbers ──► [1, 2, 3]
                   ▲
                   │
changeFirst: arr ──┘
La copia del riferimento punta allo stesso oggetto in memoria

Modificando arr[0], in realtà stiamo cambiando numbers[0], perché entrambi puntano allo stesso oggetto.

4. Ma se cambiamo il riferimento stesso, l'oggetto originario non cambierà!

Ora proviamo così:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] numbers = {1, 2, 3};
        replaceArray(numbers);
        System.out.println(numbers[0]); // Cosa stamperà?
    }

    public static void replaceArray(int[] arr) 
    {
        arr = new int[] {10, 20, 30};
    }
}

Risultato: stamperà 1.

Perché?
Nel metodo replaceArray la variabile arr inizia a puntare a un nuovo array, ma ciò non influisce in alcun modo sulla variabile numbers in main.
Prima erano legate dallo stesso riferimento, ma quando all'interno del metodo assegnamo arr = ..., si tratta solo di una modifica locale.

Analogia
È come se tu e un amico aveste la stessa chiave dell'appartamento. L'amico si fa una nuova chiave, ma la tua vecchia chiave resta la stessa e la porta è sempre quella.

5. Esempi per fissare le idee: non si possono «scambiare» due int tramite un metodo

Molti principianti provano a scrivere un metodo per scambiare due numeri, ma non funziona:

public class Demo 
{
    public static void main(String[] args) 
    {
        int a = 5, b = 10;
        swap(a, b);
        System.out.println(a + " " + b); // Atteso: 10 5, ma in realtà?
    }

    public static void swap(int x, int y) 
    {
        int temp = x;
        x = y;
        y = temp;
    }
}

Risultato: sullo schermo verranno stampati 5 10.

Perché?
Perché in swap arrivano copie delle variabili a e b. Tutto ciò che facciamo all'interno di swap influisce solo su queste copie, non sugli originali.

6. Ma con gli array si può cambiare il contenuto!

Vediamo il modo corretto di scambiare i valori in un array:

public class Demo 
{
    public static void main(String[] args) 
    {
        int[] arr = {5, 10};
        swap(arr);
        System.out.println(arr[0] + " " + arr[1]); // Atteso: 10 5
    }

    public static void swap(int[] arr) 
    {
        int temp = arr[0];
        arr[0] = arr[1];
        arr[1] = temp;
    }
}

Risultato: stamperà 10 5.

Perché?
Perché stiamo modificando il contenuto dell'oggetto (l'array) a cui puntano sia la variabile originale sia il parametro del metodo.

7. Dimostrazione: oggetti e i loro campi

Con gli array abbiamo chiarito. E per gli oggetti delle nostre classi? È esattamente lo stesso!

class Box 
{
    int value;
}

public class Demo
{
    public static void main(String[] args) 
    {
        Box box = new Box();
        box.value = 7;
        changeBox(box);
        System.out.println(box.value); // Cosa stamperà?
    }

    public static void changeBox(Box b) 
    {
        b.value = 42;
    }
}

Risultato: stamperà 42.

Perché?
Perché stiamo modificando un campo dell'oggetto a cui punta il riferimento.

Cosa accade durante il passaggio dei parametri ai metodi

Cosa passiamo Che cosa viene copiato Si può modificare dentro il metodo? Le modifiche sono visibili all'esterno?
int, double ecc. Copia del valore Sì, ma solo la copia No
Array Copia del riferimento Sì, se si modificano gli elementi Sì
Oggetto (classe) Copia del riferimento Sì, se si modificano i campi Sì
Array/oggetto Copia del riferimento No, se si assegna un nuovo riferimento No

8. Errori tipici

Errore n. 1: aspettarsi che un int cambi dopo la chiamata del metodo. Molti principianti pensano che, passando una variabile di tipo int a un metodo, sia possibile cambiarne il valore all'interno del metodo. Non è vero: cambia solo la copia!

Errore n. 2: tentare di scambiare due int tramite un metodo. Come abbiamo visto, è impossibile — si scambiano solo le copie delle variabili, gli originali non vengono toccati.

Errore n. 3: tentare di «sostituire» un array tramite un metodo. Se dentro il metodo assegnate al parametro-array un nuovo array (arr = new int[]{...}), ciò non influenzerà l'array originale. Per «sostituire» un array — bisogna restituire il nuovo array dal metodo e assegnarlo alla variabile nel codice chiamante.

Errore n. 4: dimenticare che si può modificare il contenuto di un oggetto. Se passate un oggetto (o un array) — ricordate che qualsiasi modifica ai suoi campi/elementi sarà visibile all'esterno!

Errore n. 5: confusione con null. Se passate a un metodo un riferimento uguale a null e provate ad accedere ai suoi campi — otterrete una NullPointerException. Fate attenzione!

1
Compito
JAVA 25 SELF, livello 8, lezione 4
Bloccato
Azzera il punto di partenza del robot sulla traiettoria 🤖
Azzera il punto di partenza del robot sulla traiettoria 🤖
1
Compito
JAVA 25 SELF, livello 8, lezione 4
Bloccato
Tentativo di aggiornare la lista dei bonus del giocatore 🎁
Tentativo di aggiornare la lista dei bonus del giocatore 🎁
1
Compito
JAVA 25 SELF, livello 8, lezione 4
Bloccato
Scambio dei giocatori in una squadra di calcio ⚽
Scambio dei giocatori in una squadra di calcio ⚽
1
Compito
JAVA 25 SELF, livello 8, lezione 4
Bloccato
Aggiornamento del contenuto della scatola magica 📦
Aggiornamento del contenuto della scatola magica 📦
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION