Considero final
nei parametri del metodo e nelle variabili locali come rumore del codice. Le dichiarazioni dei metodi Java possono essere piuttosto lunghe (specialmente con i generici) - non è necessario renderle più a lungo.
Se i test unitari sono scritti correttamente, l'assegnazione ai parametri "dannosi" verrà rilevata, quindi non dovrebbe mai effettivamente essere un problema. La chiarezza visiva è più importante che evitare un errore possibile che non viene rilevato perché i test dell'unità hanno copertura insufficiente.
Strumenti come FindBugs e CheckStyle che possono essere configurati per interrompere la compilazione se l'assegnazione viene effettuata su parametri o variabili locali, se ti interessa molto di queste cose.
Naturalmente, se hai bisogno di per renderli definitivi, ad esempio perché stai utilizzando il valore in una classe anonima, allora nessun problema: questa è la soluzione più semplice e pulita.
Oltre all'ovvio effetto di aggiungere parole chiave extra ai parametri, e quindi IMHO mimetizzandoli, l'aggiunta di parametri finali ai metodi spesso rende il codice nel corpo del metodo meno leggibile, il che rende il codice peggiore - per essere "buono" ", il codice deve essere leggibile e il più semplice possibile. Per un esempio forzato, diciamo che ho un metodo che ha bisogno di lavorare senza distinzione tra maiuscole e minuscole.
Senza final
:
public void doSomething(String input) {
input = input.toLowerCase();
// do a few things with input
}
Semplice. Pulito. Tutti sanno cosa sta succedendo.
Ora con 'final', opzione 1:
public void doSomething(final String input) {
final String lowercaseInput = input.toLowerCase();
// do a few things with lowercaseInput
}
Pur facendo in modo che i parametri final
interrompano l'aggiunta del codice da parte del programmatore che sta lavorando con il valore originale, c'è lo stesso rischio che il codice più in basso possa usare input
invece di lowercaseInput
, che non dovrebbe e che non può essere protetto, perché non puoi toglierlo dal campo di applicazione (o anche assegnare null
a input
se ciò sarebbe di aiuto in ogni caso).
Con "final", opzione 2:
public void doSomething(final String input) {
// do a few things with input.toLowerCase()
}
Ora abbiamo appena creato ancora più rumore di codice e introdotto un calo di prestazioni dovuto al fatto di dover richiamare toLowerCase()
n volte.
Con "final", opzione 3:
public void doSomething(final String input) {
doSomethingPrivate(input.toLowerCase());
}
/** @throws IllegalArgumentException if input not all lower case */
private void doSomethingPrivate(final String input) {
if (!input.equals(input.toLowerCase())) {
throw new IllegalArgumentException("input not lowercase");
}
// do a few things with input
}
Parla del rumore del codice. Questo è un disastro ferroviario. Abbiamo un nuovo metodo, un blocco di eccezioni richiesto, perché altri codici potrebbero invocarlo in modo errato. Altri test unitari per coprire l'eccezione. Tutto per evitare una linea semplice, e IMHO preferibile e innocua.
C'è anche il problema che i metodi non dovrebbero essere così lunghi che non puoi facilmente visualizzarli visivamente e sapere a colpo d'occhio che un'assegnazione ai parametri è avvenuta.
Penso che sia una buona pratica / stile che se si assegna ad un parametro lo si fa ogni volta nel metodo, preferibilmente in prima linea o subito dopo il controllo di input di base, in modo efficace in sostituzione per il intero metodo, che ha un effetto coerente all'interno del metodo. I lettori sanno di aspettarsi che ogni incarico sia ovvio (vicino alla dichiarazione della firma) e in un luogo coerente, il che riduce notevolmente il problema che l'aggiunta di final sta cercando di evitare. In realtà raramente assegno ai parametri, ma se lo faccio lo faccio sempre all'inizio di un metodo.
Nota anche che final
in realtà non ti protegge come potrebbe sembrare a prima vista:
public void foo(final Date date) {
date.setTime(0);
// code that uses date
}
final
non ti protegge completamente a meno che il tipo di parametro non sia primitivo o immutabile.