2016-02-23 34 views
7

In un metodo ho questo:Perché una variabile locale in Java non è considerata "efficacemente definitiva" anche se nulla lo modifica in seguito?

int x = 0 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} 

if (x != 0) { 
    doLater(() -> showErrorMessage(x)); // compile error here 
} 

// no more reference to 'x' here 

non capisco il motivo per cui produce errore di compilazione. L'errore dice che x non è definitivo o effettivamente definitivo, quindi non è possibile accedervi dal corpo lambda. Non è stata apportata alcuna modifica a x dopo la chiamata doLater, pertanto il valore di x è già determinato quando viene chiamato doLater.

Sto indovinando che la risposta a questa domanda è perché x non è qualificato per essere chiamato una variabile efficacemente finale. Tuttavia, voglio sapere qual è la ragione.

Non può il compilatore solo creare una variabile temporanea finale, di fatto rendendo il codice come:

if (x != 0) { 
    final int final_x = x; 
    doLater(() -> showErrorMessage(final_x)); 
} 

e tutto continua a funzionare lo stesso?

+1

Perché pensi che il compilatore sarà in grado di farlo? – Tunaki

+1

Il compilatore può tenere traccia dell'ultimo posto in cui una variabile viene modificata, tranne che non è ciò che fa. –

+0

@Tunaki perché sa che x non viene modificato dopo il lambda, quindi può sempre garantire che x sia già stato risolto dal momento in cui viene utilizzato. – yuku

risposta

15

Efficacemente finale significa che potrebbe essere stato effettuato final cioè non cambia mai. Significa che in modo efficace, la variabile potrebbe essere uno final.

Il problema è che non tiene traccia dell'ultima volta che l'hai cambiato, ma piuttosto, l'hai mai cambiato. Modificare l'istruzione if a

int x; 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} else { 
    x = 0; 
} 

o

int x = isA() ? 1 : 
     isB() ? 2 : 0; 
+0

Probabilmente la mia domanda non è esattamente corretta. Quello che mi chiedo è perché la specifica di una variabile 'effective-final' non si applica al mio codice di esempio? Più come "perché la decisione del JLS non è così?". – yuku

+0

@yuku Una variabile o un campo finale può essere assegnato per definizione solo una volta. Per una variabile, di solito è in un inizializzatore. Per un campo, che di solito è nel costruttore. La frase chiave è * assegnata esattamente una volta *, che è la cartina di tornasole per la finale. * Effettivamente finale * significa semplicemente che si tratta di atti definitivi senza essere dichiarati così. Quando si inizializza su 0, * quindi * si passa a 1 o 2, non è definitivo in alcun significato della parola. – Andreas

4

La tua variabile xsarebbe stata effettivamente definitiva è stata inizializzata una volta e non è stata nuovamente modificata in nessun caso. Se avessi solo:

int x = 0; 
doLater(() -> showErrorMessage(x)); 

quindi avrebbe compilato.

Tuttavia, aggiungendo condizioni che potrebbero modificare il valore della variabile

int x = 0; 
if (isA()) { 
    x = 1; 
} else if (isB()) { 
    x = 2; 
} 

rende la variabile non essendo efficacemente finale e quindi l'errore di compilazione è sorta.


Inoltre, dal momento che questo puntatore approccio hai implementato non avrebbe funzionato, si potrebbe refactoring del codice un po 'ad una semplice istruzione else if-:

if (isA()) { 
    doLater(() -> showErrorMessage(1)); 
} else if (isB()) { 
    doLater(() -> showErrorMessage(2)); 
} 

e completamente sbarazzarsi di x.

2

Versione corta, una variabile è efficacemente finale se è assegnato esattamente una volta, non importa quale percorso di codice viene eseguito.

Versione lunga, citando Java Language Specification 4.12.4. final Variables (sottolineatura mia):

Alcune variabili che non sono dichiarati final sono invece considerati efficacemente finale:

  • Una variabile locale il cui dichiaratore ha un inizializzatore (§14.4.2) è effettivamente finale se tutto il follo le ali sono vere:
    • Non è dichiarato final.
    • Non si verifica mai come lato sinistro in un'espressione di assegnazione (§15.26). (Si noti che il dichiaratore variabile locale contenente l'inizializzatore è non un'espressione di assegnazione.)
    • Esso non avviene come operando di un incremento prefisso o suffisso o operatore di decremento (§15.14, §15.15).

Ora, si può rendere efficace finale eliminando l'inizializzatore, perché continua:

  • Una variabile locale il cui dichiaratore manca un inizializzatore è efficacemente finale se tutte le seguenti condizioni sono vere:
    • Non è dichiarato final.
    • Ogni volta che si verifica come il lato sinistro in un'espressione di assegnazione, è sicuramente non assegnata e non assegnata definitivamente prima dell'assegnazione; cioè, è definitivamente non assegnato e non assegnato definitivamente dopo il lato destro dell'espressione di assegnazione (§16 (Definite Assignment)).
    • Non si verifica mai come operando di un operatore di incremento o decremento prefisso o postfisso.