Parlando come sviluppatore Java che rende tutte le variabili final
di default (e chi apprezza il fatto che Eclipse possa farlo automaticamente), trovo più facile ragionare sul mio programma se le variabili sono inizializzate una volta e non sono mai cambiate di nuovo.
Per prima cosa, le variabili non inizializzate non sono più un problema, perché provare a utilizzare una variabile final
prima che sia stata inizializzata comporterà un errore di compilazione. Questo è particolarmente utile per la logica condizionale nidificata, in cui voglio essere sicuro di aver coperto tutti i casi:
final int result;
if (/* something */) {
if (/* something else */) {
result = 1;
}
else if (/* some other thing */) {
result = 2;
}
}
else {
result = 3;
}
System.out.println(result);
Ho coperto tutti i casi? (Suggerimento: No.) Abbastanza sicuro, questo codice non verrà nemmeno compilato.
Un'altra cosa: in generale, ogni volta che sai che qualcosa è sempre vero riguardo a una variabile, dovresti cercare di farla applicare alla tua lingua. Lo facciamo ogni giorno quando specifichiamo il tipo di una variabile, ovviamente: la lingua assicurerà che i valori che non sono di quel tipo non possano essere memorizzati in quella variabile. Allo stesso modo, se sai che una variabile non deve essere riassegnata perché ha già il valore che dovrebbe conservare per l'intero metodo, puoi ottenere la lingua per far rispettare tale restrizione dichiarandola final
.
Infine, c'è una questione di abitudine. Altri hanno affermato che questa è un'abitudine (da +1 a Jon per ), ma lasciatemi dire qualcosa sul perché vorresti questa abitudine. Se stai dichiarando campi nella tua classe e non variabili locali in un metodo, allora è possibile che più thread accedano a quei campi nello stesso momento. Esistono alcune oscure eccezioni, ma in generale, se un campo è definitivo, ogni thread che utilizza la classe vedrà lo stesso valore per la variabile. Al contrario, se un campo non è definitivo e più thread stanno usando la tua classe, dovrai preoccuparti della sincronizzazione esplicita usando synchronized
blocchi e / o classi da java.util.concurrent
. La sincronizzazione è possibile, ma la programmazione è già abbastanza difficile. ;-) Quindi, se dichiari solo tutto final
per abitudine, molti dei tuoi campi saranno definitivi e trascorrerai il minor tempo possibile a preoccuparti della sincronizzazione e dei bug relativi alla concorrenza .
Per ulteriori informazioni su questa abitudine, consulta il suggerimento "Riduci la mutabilità" in efficace di Joshua Bloch Java .
Modifica: @Peter Taylor ha sottolineato che l'esempio sopra non verrebbe compilato anche se la parola chiave final
viene rimossa, il che è completamente corretto. Quando ho consigliato di mantenere tutte le variabili locali finali, è perché volevo rendere esempi come il seguente impossibile:
int result = 0;
// OK, time to cover all the cases!
if (/* something */) {
if (/* something else */) {
result = 1;
}
else if (/* some other thing */) {
result = 2;
}
// Whoops, missed an "else" here. Too bad.
}
else {
result = 3;
}
System.out.println(result); // Works fine!
L'uso di una nuova variabile invece di riutilizzare una vecchia è come posso dire al compilatore che cerca di coprire l'universo completo di possibilità, e usando final
variabili mi costringe a usare una nuova variabile invece di riciclarne una vecchia.
Un altro reclamo valido su questo esempio è che dovresti evitare la logica condizionale nidificata complessa in primo luogo. Questo è vero, certo, proprio perché è difficile accertarsi di aver coperto tutti i casi nel modo che intendevi. Tuttavia, a volte la logica complessa non può essere evitata. Quando la mia logica è complessa, voglio che le mie variabili siano il più semplici possibili e che posso ottenere assicurandomi che i valori delle mie variabili non cambino mai dopo essere stati inizializzati.