Diferença entre final e efetivamente final

Eu estou jogando com lambdas no Java 8 e me deparei com aviso de local variables referenced from a lambda expression must be final or effectively final . Eu sei que quando eu uso variables ​​dentro de uma class anônima, elas devem ser finais na class externa, mas ainda assim – qual é a diferença entre final e efetivamente final ?

… a partir do Java SE 8, uma class local pode acessar variables ​​locais e parâmetros do bloco delimitador final ou efetivamente final. Uma variável ou parâmetro cujo valor nunca é alterado após ser inicializado é efetivamente final.

Por exemplo, suponha que a variável numberLength não seja declarada final e você adicione a instrução de atribuição marcada no construtor 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; } ... } ... } 

Devido a esta declaração de atribuição, a variável numberLength não é mais efetivamente final. Como resultado, o compilador Java gera uma mensagem de erro semelhante a "variables ​​locais referenciadas a partir de uma class interna deve ser final ou efetivamente final", onde a class interna PhoneNumber tenta acessar a variável numberLength:

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

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

Acho que a maneira mais simples de explicar “efetivamente final” é imaginar adicionar o modificador final a uma declaração de variável. Se, com essa alteração, o programa continuar a se comportar da mesma maneira, em tempo de compilation e em tempo de execução, essa variável será efetivamente final.

Segundo os docs :

Uma variável ou parâmetro cujo valor nunca é alterado após ser inicializado é efetivamente final.

Basicamente, se o compilador descobrir que uma variável não aparece em atribuições fora de sua boot, então a variável é considerada efetivamente final .

Por exemplo, considere alguma 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; } } 

De um artigo de “Brian Goetz”,

‘Efetivamente final’ é uma variável que não causaria erro de compilador se fosse anexada por ‘final’

lambda-state-final- Brian Goetz

Esta variável abaixo é final , por isso não podemos alterar o seu valor depois de inicializado. Se tentarmos obter um erro de compilation …

 final int variable = 123; 

Mas se criarmos uma variável como essa, podemos mudar seu valor …

 int variable = 123; variable = 456; 

Mas no Java 8 , todas as variables ​​são finais por padrão. Mas a existência da segunda linha no código torna-a não final . Então, se removermos a segunda linha do código acima, nossa variável agora é “efetivamente final”

 int variable = 123; 

Então … Qualquer variável que é atribuída uma vez e apenas uma vez, é “efetivamente final” .

Uma variável é final ou efetivamente final quando é inicializada uma vez e nunca é transformada em sua class proprietária. E não podemos inicializá- lo em loops ou classs internas .

Final :

 final int number; number = 23; 

Efetivamente Final :

 int number; number = 34; 

Quando uma expressão lambda usa uma variável local atribuída de seu espaço envolvente, há uma restrição importante. Uma expressão lambda só pode usar uma variável local cujo valor não seja alterado. Essa restrição é referida como ” captura de variável “, que é descrita como; Valores de captura de expressão lambda, não variables .
As variables ​​locais que uma expressão lambda pode usar são conhecidas como ” efetivamente finais “.
Uma variável efetivamente final é aquela cujo valor não muda depois de ser atribuído pela primeira vez. Não há necessidade de declarar explicitamente tal variável como final, embora isso não seja um erro.
Vamos ver isso com um exemplo, temos uma variável local i que é inicializada com o valor 7, com a expressão lambda estamos tentando mudar esse valor atribuindo um novo valor para i. Isso resultará em erro do compilador – ” A variável local i definida em um escopo de fechamento deve ser final ou efetivamente final

 @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; }; } } 

O tópico final efetivo é descrito em JLS 4.12.4 e o último parágrafo consiste em uma explicação clara:

Se uma variável for efetivamente final, adicionar o modificador final à sua declaração não introduzirá erros de tempo de compilation. Por outro lado, uma variável ou parâmetro local que é declarado final em um programa válido torna-se efetivamente final se o modificador final for removido.

 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; } } } 

Como outros já disseram, uma variável ou parâmetro cujo valor nunca é alterado depois de inicializado é efetivamente final. No código acima, se você alterar o valor de x na class interna FirstLevel , o compilador fornecerá a mensagem de erro:

Variáveis ​​locais referenciadas a partir de uma expressão lambda devem ser finais ou efetivamente finais.

Se você pudesse adicionar o modificador final a uma variável local, seria efetivamente final.

Expressões lambda podem acessar

  • variables ​​estáticas,

  • variables ​​de instância,

  • parâmetros efetivamente finais do método, e

  • efetivamente variables ​​locais finais.

Fonte: OCP: Guia de estudo do programador II do Java SE 8 da Oracle Certified Professional, Jeanne Boyarsky, Scott Selikoff

Além disso,

Uma variável effectively final é uma variável cujo valor nunca é alterado, mas não é declarado com a palavra-chave final .

Fonte: Começando com Java: de estruturas de controle através de objects (6ª edição), Tony Gaddis

Além disso, não esqueça o significado de final que é inicializado exatamente uma vez antes de ser usado pela primeira vez.

No entanto, a partir do Java SE 8, uma class local pode acessar variables ​​locais e parâmetros do bloco delimitador que são finais ou efetivamente finais.

Isso não começou no Java 8, eu uso isso há muito tempo. Este código usado (antes do java 8) para ser legal:

 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 } );