Una domanda che mi fanno molti o che mi capita di leggere spesso riguardo JAVA è: “come faccio a passare dei parametri per riferimento?”. La mia risposta è: “NON SI PUÒ!!”.
Ecco un mito da sfatare:
“Gli oggetti sono passati per riferimento, i primitivi per valore” FALSO
In JAVA ogni cosa è passata per valore. Gli oggetti, non vengono passati affatto. I valori delle variabili sono sempre o primitive o riferimenti, mai oggetti!
Prima di tutto, definiamo cosa significa “passaggio per riferimento”. Una delle definizioni che preferisco è quella di Dale King scritta nel ng comp.lang.java.help:
“The lvalue of the formal parameter is set to the lvalue of the actual parameter”.
Quindi se ad esempio scriviamo questo codice:
Object x = null;
giveMeAString(x);
System.out.println(x);
[...]
void giveMeAString(Object y) { y = “Hello, World!”; }
il risultato, se JAVA usasse il passaggio per riferimento, sarebbe:
Hello, World!
mentre il risultato è
null
ed è chiaro perché, alla luce di ciò che è stato scritto sopra.
Questa è una scelta di progettazione, non è una limitazione del linguaggio come si potrebbe pensare. Una delle ragioni per cui gli sviluppatori hanno escluso la semantica del “passaggio per riferimento” è che ciò mischierebbe l’input e l’output del codice.
In Java un programmatore può asserire che il valore delle variabili non cambierà quando vengono passate come parametri ad un metodo.
Inoltre il “passaggio per riferimento” rende estremamente complesso definire l’inerfaccia di un metodo.
Chris Smith descrive due situazioni in cui può essere necessario il “passaggio per riferimento”, per ognuna presenta una soluzione:
- Molti linguaggi usano il passaggio per riferimeno per ridurre il “costo” della chiamata di un metodo, prevenendo la copia di una grossa quantita’ di dati. Questo non è un problema in Java. In Java i valori passati a un metodo sono o dati primitivi o il reference di un oggetti.
- Il passaggio per riferimenti permette alle variabili di essere modificate.La soluzione è di effettuare un refactor dell’applicazione per usare return value in modo da propagare le modifiche al client.
a = someMethod(a);
Ecco delle linee guida:
- Per return values che indicano lo stato, il successo o meno di un metodo: eliminateli immediamente! Sostituite tutto con la gestione delle eccezioni.
- Trovare gruppi di return value e incapsularli in oggetti che contentono ogni informazione in dei campi. Le classi di questi oggetti possono poi essere estese per incapsulare il loro comportamento, in questo modo si migliora la progettazione del codice. Ogni serie di return value che si incapsula in un oggetto rimuove i valori di ritorno dal metodo e aumenta il livello di astrazione dell’interfaccia del metodo. Per esempio, invece di passare le coordinate X e Y per riferimento, creare una classe Punto, passare il riferimento di un oggetto per valore e aggiornare i valori dell’oggetto all’interno del metodo.
- Se capita di passare un oggetto e poi ritornare una nuova versione dell’oggetto, prendere in considerazione di spostare il metodo per renderlo membro della classe dell’oggetto che stavi passando. Questo migliora l’incapsulamento e semplifica l’interfaccia.
- Se dopo tutte queste fasi hai ancora piu’ valori di ritorno per un metodo, dividi il metodo in differenti metodi in modo che ognuno restituisca parte della risposta. Utilizzando questa serie di metodi il codice sarà molto più chiaro che usare un mega-metodo :) .
Buon refactoring!