Perché dichiarare le variabili finali all'interno dei metodi? [duplicare]

70

Studiando alcune classi di Android, mi sono reso conto che la maggior parte delle variabili dei metodi sono dichiarati come finali.

Esempio di codice preso dalla classe android.widget.ListView:

/**
* @return Whether the list needs to show the top fading edge
*/
private boolean showingTopFadingEdge() {
    final int listTop = mScrollY + mListPadding.top;
    return (mFirstPosition > 0) || (getChildAt(0).getTop() > listTop);
}

/**
 * @return Whether the list needs to show the bottom fading edge
 */
private boolean showingBottomFadingEdge() {
    final int childCount = getChildCount();
    final int bottomOfBottomChild = getChildAt(childCount - 1).getBottom();
    final int lastVisiblePosition = mFirstPosition + childCount - 1;
    final int listBottom = mScrollY + getHeight() - mListPadding.bottom;
    return (lastVisiblePosition < mItemCount - 1)
                     || (bottomOfBottomChild < listBottom);
}

Qual è l'intenzione di utilizzare la parola chiave finale in questi casi?

    
posta Rodrigo 22.10.2011 - 06:43
fonte

7 risposte

59

Direi che ciò è dovuto alla forza dell'abitudine . Il programmatore che ha scritto questo codice sapeva come stava scrivendo che i valori per le variabili finali non dovrebbero mai essere cambiati dopo l'assegnazione, e quindi li hanno resi definitivi. Qualsiasi tentativo di assegnare un nuovo valore a una variabile finale dopo l'assegnazione comporterà un errore del compilatore.

Come abitudine, non è male svilupparsi. Come minimo, fare una variabile finale specifica l' intento del programmatore al momento della scrittura. Questo è importante in quanto potrebbe dare ai successivi programmatori che modificano la pausa del codice per il pensiero prima di iniziare a modificare il modo in cui viene utilizzata tale variabile.

    
risposta data 22.10.2011 - 10:13
fonte
51

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.

    
risposta data 22.10.2011 - 13:40
fonte
7

Non possiamo conoscere la risposta di sicuro (a meno che non chiediamo agli sviluppatori originali), ma le mie supposizioni sono:

  1. I programmatori di questi metodi potrebbero aver pensato che l'aggiunta di "finale" esprimesse meglio lo scopo di quelle variabili.
  2. I programmatori potrebbero utilizzare uno strumento che aggiunge automaticamente "finale" dove può.
  3. Potrebbe essere un trucco di ottimizzazione: non ho le conoscenze / strumenti per verificarlo da solo, ma sarei interessato a sapere in entrambi i modi.
risposta data 22.10.2011 - 07:56
fonte
6

Un altro motivo è di poterli usare nelle classi anonime interne:

public interface MyInterface {
    void foo();
} 

void myMethod() {
    final String asdf = "tada";//if not final cannot be used in inner classes
    new MyInterface() {
        @Override
        public void foo() {
             String myNewString = asdf;//does not compile only if asdf is final
        }
    }
}
    
risposta data 09.08.2013 - 17:31
fonte
4

Per rendere gli errori sciocchi meno frequenti. final si assicura che la variabile punti sempre al suo oggetto assegnato per primo e ogni tentativo di modifica verrà conteggiato come errore in fase di compilazione.

Potresti anche chiedere "perché dichiarare esplicitamente il tipo di una variabile?", o "perché dichiarare metodi come costanti in C ++?". La stessa ragione.

    
risposta data 22.10.2011 - 14:39
fonte
2

Ecco il motivo per cui: Evitare la riassegnazione a variabili / parametri locali. Ciò aumenterebbe la leggibilità ed eviterebbe alcuni bug stupidi.

link

Estratto:

Java 1.1 and later versions allow you to mark a parameter as final; this prevents assignment to the variable. It still allows you to modify the object the variable refers to. I always treat my parameters as final, but I confess I rarely mark them so in the parameter list.

    
risposta data 09.08.2013 - 17:22
fonte
0

Onestamente, non penso che ci sarebbe un vero motivo per fare una variabile finale in questo contesto. Suppongo che abbiano copiato e incollato il codice, o semplicemente vogliono davvero scoraggiare chiunque aggiunga più codice a quel metodo dal fare riassegnazioni a quelle variabili.

    
risposta data 22.10.2011 - 07:18
fonte

Leggi altre domande sui tag