Variável usada na expressão lambda deve ser final ou efetivamente final

Variável usada na expressão lambda deve ser final ou efetivamente final

Quando tento usar o calTz ele está mostrando esse erro.

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { try { cal.getComponents().getComponents("VTIMEZONE").forEach(component->{ VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(calTz==null) { calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); } }); } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return null; } 

Uma variável final significa que ela pode ser instanciada apenas uma vez. em Java, você não pode usar variables ​​não finais em lambda, bem como em classs internas anônimas.

Você pode refatorar seu código com o antigo loop for-each:

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { try { for(Component component : cal.getComponents().getComponents("VTIMEZONE")) { VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(calTz==null) { calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); } } } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return null; } 

Mesmo que eu não consiga entender algumas partes deste código:

  • você chama um v.getTimeZoneId(); sem usar seu valor de retorno
  • com a atribuição calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); você não modifica o calTz originalmente passado e você não o usa neste método
  • Você sempre retorna null , por que você não define void como tipo de retorno?

Espero também que essas dicas ajudem você a melhorar.

De um lambda, você não pode obter uma referência a qualquer coisa que não seja final. Você precisa declarar um wrapper final de fora da lamda para manter sua variável.

Eu adicionei o object final de ‘referência’ como este wrapper.

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { final AtomicReference reference = new AtomicReference<>(); try { cal.getComponents().getComponents("VTIMEZONE").forEach(component->{ VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(reference.get()==null) { reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue())); } }); } catch (Exception e) { //log.warn("Unable to determine ical timezone", e); } return reference.get(); } 

O Java 8 tem um novo conceito chamado variável “Efetivamente final”. Isso significa que uma variável local não final cujo valor nunca muda após a boot é chamada de “Efetivamente Final”.

Esse conceito foi introduzido porque, antes do Java 8 , não poderíamos usar uma variável local não final em uma anonymous class . Se você quer ter access a uma variável local em uma anonymous class , você tem que torná-lo final.

Quando o lambda foi introduzido, essa restrição foi facilitada. Daí para a necessidade de fazer variável local final se não for alterado uma vez que é inicializado como lambda em si não é nada, mas uma class anônima.

O Java 8 percebeu a dificuldade de declarar a variável local final toda vez que o desenvolvedor usava o lambda e introduziu esse conceito e tornou desnecessário fazer as variables ​​locais final . Então, se você ver a regra para a anonymous class ainda não mudou, é só você não ter que escrever a final palavra-chave toda vez que usar o lambdas .

Eu encontrei uma boa explicação aqui

Embora outras respostas provem o requisito, elas não explicam por que o requisito existe.

O JLS menciona porque em §15.27.2 :

A restrição a variables ​​efetivamente finais proíbe o access a variables ​​locais que mudam dinamicamente, cuja captura provavelmente introduziria problemas de simultaneidade.

Para diminuir o risco de erros, eles decidiram garantir que as variables ​​capturadas nunca sejam alteradas.

No seu exemplo, você pode replace o forEach por lamdba por um loop for simples e modificar livremente qualquer variável. Ou, provavelmente, refatorie seu código para que você não precise modificar nenhuma variável. No entanto, explicarei por completo o que significa o erro e como contorná-lo.

Especificação da linguagem Java 8, §15.27.2 :

Qualquer variável local, parâmetro formal ou parâmetro de exceção usado, mas não declarado em uma expressão lambda, deve ser declarado final ou ser efetivamente final ( §4.12.4 ) ou um erro de tempo de compilation ocorre quando o uso é tentado.

Basicamente você não pode modificar uma variável local ( calTz neste caso) de dentro de um lambda (ou uma class local / anônima). Para conseguir isso em Java, você tem que usar um object mutável e modificá-lo (através de uma variável final) do lambda. Um exemplo de object mutável aqui seria uma matriz de um elemento:

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) { TimeZone[] result = { null }; try { cal.getComponents().getComponents("VTIMEZONE").forEach(component -> { ... result[0] = ...; ... } } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return result[0]; } 

se não for necessário modificar a variável do que uma solução geral para esse tipo de problema, seria extrair a parte do código que usa lambda e usar a palavra-chave final no método-parâmetro.