Estou brincando com lambdas no Java 8 e me deparei com um aviso local variables referenced from a lambda expression must be final or effectively final
. Eu sei que quando eu uso variáveis dentro da classe anônima, elas devem ser finais na classe externa, mas ainda assim - qual é a diferença entre final e efetivamente final ?
350
Respostas:
Por exemplo, suponha que a variável
numberLength
não seja declarada final e você adicione a instrução de atribuição marcada noPhoneNumber
construtor:Por causa dessa declaração de atribuição, a variável numberLength não é mais definitiva. Como resultado, o compilador Java gera uma mensagem de erro semelhante a "variáveis locais referenciadas de uma classe interna devem ser finais ou efetivamente finais" onde a classe 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/localclasses.html
fonte
numberLength
se torne uma variável local desse método.Acho que a maneira mais simples de explicar "efetivamente final" é imaginar adicionar o
final
modificador a uma declaração de variável. Se, com essa alteração, o programa continuar a se comportar da mesma maneira, tanto em tempo de compilação quanto em tempo de execução, essa variável será efetivamente final.fonte
case k
requer uma expressão constante que pode ser uma variável constante ("Uma variável constante é uma variável final do tipo ou tipo primitivo String que é inicializada com uma expressão constante" JLS 4.12.4 ), que é um caso especial de uma final variável.De acordo com os documentos :
Basicamente, se o compilador descobrir que uma variável não aparece nas atribuições fora de sua inicialização, a variável será considerada efetivamente final .
Por exemplo, considere alguma classe:
fonte
bar
no seu exemplo não é uma variável local, mas um campo. "Efetivamente final" na mensagem de erro como acima não se aplica aos campos.bar
é um parâmetro aqui, não um campo.'Efetivamente final' é uma variável que não daria erro ao compilador se fosse anexado por 'final'
De um artigo de 'Brian Goetz',
final de estado de lambda- Brian Goetz
fonte
Essa variável abaixo é final , portanto, não podemos alterar seu valor uma vez inicializado. Se tentarmos, obteremos um erro de compilação ...
Mas se criarmos uma variável como essa, podemos mudar seu valor ...
Mas no Java 8 , todas as variáveis são finais por padrão. Mas a existência da 2ª linha no código a torna não final . Portanto, se removermos a segunda linha do código acima, nossa variável agora é "efetivamente final" ...
Portanto, qualquer variável atribuída uma e apenas uma vez é "efetivamente final" .
fonte
Uma variável é final ou efetivamente final quando é inicializada uma vez e nunca é alterada em sua classe de proprietário. E não podemos inicializá- lo em loops ou classes internas .
Final :
Efetivamente Final :
fonte
Quando uma expressão lambda usa uma variável local atribuída de seu espaço em anexo, há uma restrição importante. Uma expressão lambda pode usar apenas variável local cujo valor não muda. Essa restrição é referida como " captura variável ", que é descrita como; valores de captura de expressão lambda, não variáveis .
As variáveis locais que uma expressão lambda pode usar são conhecidas como " efetivamente finais ". Vamos ver com um exemplo: temos uma variável local i que é inicializada com o valor 7; na expressão lambda, estamos tentando alterar esse valor atribuindo um novo valor a i. Isso resultará em erro do compilador - "
Uma variável efetivamente final é aquela cujo valor não muda após a primeira atribuição. Não há necessidade de declarar explicitamente uma variável como final, embora isso não seja um erro.
fonte
O tópico final efetivo é descrito no JLS 4.12.4 e o último parágrafo consiste em uma explicação clara:
fonte
final é uma variável declarada com palavra-chave
final
, exemplo:permanece
final
durante todo o programa.efetivamente final : qualquer variável local ou parâmetro ao qual seja atribuído um valor apenas uma vez no momento (ou atualizado apenas uma vez). Pode não permanecer efetivamente final durante todo o programa. portanto, isso significa que a variável efetivamente final pode perder sua propriedade efetivamente final após o tempo em que é atribuída / atualizada pelo menos mais uma atribuição. exemplo:
fonte
final
palavra-chave a uma declaração sem introduzir erros de compilação, ela não será efetivamente final . É o contrapositivo dessa declaração: "Se uma variável for efetivamente final, adicionar o modificador final à sua declaração não introduzirá nenhum erro em tempo de compilação".Como já foi dito, uma variável ou parâmetro cujo valor nunca é alterado após a inicialização é efetivamente final. No código acima, se você alterar o valor de
x
na classe internaFirstLevel
, o compilador exibirá a mensagem de erro:fonte
Expressões lambda podem acessar
variáveis estáticas,
variáveis de instância,
efetivamente parâmetros finais do método, e
efetivamente variáveis locais finais.
Fonte: OCP: Guia de Estudo do Oracle Certified Professional Java SE 8 Programmer II da Oracle, Jeanne Boyarsky, Scott Selikoff
Além disso,
Fonte: Iniciando com Java: das estruturas de controle aos objetos (6ª edição), Tony Gaddis
Além disso, não esqueça o significado de
final
que ele foi inicializado exatamente uma vez antes de ser usado pela primeira vez.fonte
Declarar uma variável
final
ou nãofinal
, mas mantê-la efetivamente final pode resultar (depende do compilador) em bytecode diferente.Vamos dar uma olhada em um pequeno exemplo:
O bytecode correspondente do
main
método (Java 8u161 no Windows 64 Bit):A tabela de número de linha correspondente:
Como podemos ver o código-fonte em linhas
12
,13
,14
não aparecem no código byte. Isso é porquei
étrue
e não mudará seu estado. Portanto, este código está inacessível (mais nesta resposta ). Pela mesma razão, o código na linha9
também falha. O estado dei
não precisa ser avaliado, pois étrue
certo.Por outro lado, embora a variável
j
seja efetivamente final, ela não é processada da mesma maneira. Não existem essas otimizações aplicadas. O estado dej
é avaliado duas vezes. O bytecode é o mesmo, independentemente dej
ser efetivamente final .fonte
A variável Efetivamente final é uma variável local que é:
final
Enquanto uma variável final é uma variável que é:
final
palavra - chave.fonte
Isso não começou no Java 8, eu uso isso há muito tempo. Este código usado (antes do java 8) é legal:
fonte
final
variáveis podem ser acessadas, mas no Java 8 também aquelas que são efetivamente finais.