Differenza tra finale ed efficacemente finale

Sto giocando con lambdas in Java 8 e ho notato che local variables referenced from a lambda expression must be final or effectively final . So che quando uso le variabili all’interno della class anonima devono essere finali nella class esterna, ma ancora – qual è la differenza tra finale ed efficacemente finale ?

… a partire da Java SE 8, una class locale può accedere a variabili locali e parametri del blocco che li contiene, definitivi o effettivamente definitivi. Una variabile o un parametro il cui valore non viene mai modificato dopo l’inizializzazione è effettivamente definitivo.

Ad esempio, si supponga che la variabile numberLength non sia dichiarata finale e si aggiunga l’istruzione di assegnazione contrassegnata nel costruttore PhoneNumber :

 public class OutterClass { int numberLength; // <== not *final* class PhoneNumber { PhoneNumber(String phoneNumber) { numberLength = 7; // <== assignment to numberLength String currentNumber = phoneNumber.replaceAll( regularExpression, ""); if (currentNumber.length() == numberLength) formattedPhoneNumber = currentNumber; else formattedPhoneNumber = null; } ... } ... } 

A causa di questa dichiarazione di assegnazione, il numero variabile Lunghezza non è più efficacemente definitivo. Di conseguenza, il compilatore Java genera un messaggio di errore simile a "le variabili locali a cui viene fatto riferimento da una class interna deve essere finale o efficacemente finale" dove la class interna PhoneNumber tenta di accedere alla variabile numberLength:

http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html

http://docs.oracle.com/javase/tutorial/java/javaOO/localclasss.html

Trovo che il modo più semplice per spiegare “efficacemente finale” è immaginare di aggiungere il modificatore final a una dichiarazione di variabile. Se, con questo cambiamento, il programma continua a comportarsi nello stesso modo, sia in fase di compilazione che in fase di esecuzione, allora quella variabile è effettivamente definitiva.

Secondo i documenti :

Una variabile o un parametro il cui valore non viene mai modificato dopo l’inizializzazione è effettivamente definitivo.

Fondamentalmente, se il compilatore trova una variabile non appare in assegnazioni al di fuori della sua inizializzazione, allora la variabile è considerata effettivamente definitiva .

Ad esempio, considera qualche class:

 public class Foo { public void baz(int bar) { // While the next line is commented, bar is effectively final // and while it is uncommented, the assignment means it is not // effectively final. // bar = 2; } } 

Da un articolo di “Brian Goetz”,

‘Efficacemente finale’ è una variabile che non darebbe l’errore del compilatore se dovesse essere aggiunto da ‘finale’

lambda-state-final- Brian Goetz

Questa variabile di seguito è definitiva , quindi non possiamo cambiarne il valore una volta inizializzato. Se proviamo a ricevere un errore di compilazione …

 final int variable = 123; 

Ma se creiamo una variabile come questa, possiamo cambiarne il valore …

 int variable = 123; variable = 456; 

Ma in Java 8 , tutte le variabili sono definitive per impostazione predefinita. Ma l’esistenza della seconda riga nel codice lo rende non-finale . Quindi, se rimuoviamo la seconda riga dal codice precedente, la nostra variabile è ora “efficacemente definitiva”

 int variable = 123; 

Quindi .. Qualsiasi variabile che viene assegnata una sola volta, è “efficacemente definitiva” .

Una variabile è definitiva o effettivamente definitiva quando viene inizializzata una volta e non viene mai modificata nella sua class proprietario. E non possiamo inizializzarlo nei loop o nelle classi interne .

Finale :

 final int number; number = 23; 

In modo efficace finale :

 int number; number = 34; 

Quando un’espressione lambda usa una variabile locale assegnata dal suo spazio racchiudente, c’è una restrizione importante. Un’espressione lambda può utilizzare solo una variabile locale il cui valore non cambia. Tale restrizione è definita come ” cattura variabile ” che è descritta come; valori di cattura espressione lambda, non variabili .
Le variabili locali che un’espressione lambda può usare sono conosciute come ” efficacemente definitive “.
Una variabile finale efficace è una variabile il cui valore non cambia dopo che è stato assegnato per la prima volta. Non è necessario dichiarare esplicitamente tale variabile come finale, sebbene farlo non sia un errore.
Vediamolo con un esempio, abbiamo una variabile locale i che è inizializzata con il valore 7, con nell’espressione lambda stiamo provando a cambiare quel valore assegnando un nuovo valore a i. Ciò si tradurrà in errore del compilatore: ” La variabile locale definita in un ambito allegato deve essere definitiva o effettivamente definitiva

 @FunctionalInterface interface IFuncInt { int func(int num1, int num2); public String toString(); } public class LambdaVarDemo { public static void main(String[] args){ int i = 7; IFuncInt funcInt = (num1, num2) -> { i = num1 + num2; return i; }; } } 

L’ argomento finale efficace è descritto in JLS 4.12.4 e l’ultimo paragrafo è una chiara spiegazione:

Se una variabile è effettivamente finale, l’aggiunta del modificatore finale alla sua dichiarazione non introdurrà alcun errore in fase di compilazione. Viceversa, una variabile locale o parametro dichiarato finale in un programma valido diventa effettivamente definitivo se il modificatore finale viene rimosso.

 public class LambdaScopeTest { public int x = 0; class FirstLevel { public int x = 1; void methodInFirstLevel(int x) { // The following statement causes the compiler to generate // the error "local variables referenced from a lambda expression // must be final or effectively final" in statement A: // // x = 99; } } } 

Come altri hanno già detto, una variabile o un parametro il cui valore non viene mai modificato dopo l’inizializzazione è effettivamente definitivo. Nel codice precedente, se cambi il valore di x nella class interna FirstLevel il compilatore ti darà il messaggio di errore:

Le variabili locali referenziate da un’espressione lambda devono essere definitive o effettivamente definitive.

Se potessi aggiungere il modificatore final a una variabile locale, sarebbe stato effettivamente definitivo.

Le espressioni lambda possono accedere

  • variabili statiche,

  • variabili di istanza,

  • efficacemente i parametri del metodo finale, e

  • efficacemente variabili locali finali.

Fonte: OCP: Guida allo studio del programmatore II per Java SE Oracle Certified Professional, Jeanne Boyarsky, Scott Selikoff

Inoltre,

Una variabile effectively final è una variabile il cui valore non è mai cambiato, ma non è dichiarato con la parola chiave final .

Fonte: partire con Java: da Control Structures through Objects (6a edizione), Tony Gaddis

Inoltre, non dimenticare il significato di final che è inizializzato esattamente una volta prima che venga utilizzato per la prima volta.

Tuttavia, a partire da Java SE 8, una class locale può accedere alle variabili locali e ai parametri del blocco che racchiude in modo definitivo o effettivamente definitivo.

Questo non è iniziato su Java 8, lo uso da molto tempo. Questo codice usato (prima di java 8) per essere legale:

 String str = ""; //<-- not accesible from anonymous classes implementation final String strFin = ""; //<-- accesible button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String ann = str; // <---- error, must be final (IDE's gives the hint); String ann = strFin; // <---- legal; String str = "legal statement on java 7," +"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl."; //we are forced to use another name than str } );