La variable utilisée dans l’expression lambda doit être finale ou définitive

La variable utilisée dans l’expression lambda doit être finale ou définitive

Quand j’essaie d’utiliser calTz il montre cette erreur.

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

Une final variable signifie qu’elle ne peut être instanciée qu’une seule fois. en Java, vous ne pouvez pas utiliser de variables non finales dans lambda et dans les classes internes anonymes.

Vous pouvez refactoriser votre code avec l’ancienne boucle 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; } 

Même si je ne comprends pas certains morceaux de ce code:

  • vous appelez un v.getTimeZoneId(); sans utiliser sa valeur de retour
  • avec l’affectation calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); vous ne modifiez pas le calTz transmis à l’ calTz et vous ne l’utilisez pas dans cette méthode
  • Vous retournez toujours null , pourquoi ne définissez-vous void le type de retour void ?

J’espère que ces conseils vous aideront à vous améliorer.

D’un lambda, vous ne pouvez pas obtenir de référence à quelque chose qui n’est pas définitif. Vous devez déclarer un wrapper final depuis l’extérieur de la lamda pour contenir votre variable.

J’ai ajouté l’object final ‘reference’ comme cette enveloppe.

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

Java 8 a un nouveau concept appelé variable «effectivement efficace». Cela signifie qu’une variable locale non finale dont la valeur ne change jamais après l’initialisation est appelée «effectivement effective».

Ce concept a été introduit car, avant Java 8 , nous ne pouvions pas utiliser une variable locale non finale dans une anonymous class . Si vous voulez avoir access à une variable locale en anonymous class , vous devez la rendre définitive.

Lorsque lambda été introduit, cette ressortingction a été assouplie. D’où la nécessité de rendre les variables locales final si elles ne sont pas modifiées une fois qu’elles ont été initialisées, car lambda en soi n’est rien d’autre qu’une classe anonyme.

Java 8 a pris la peine de déclarer la variable locale final chaque fois que le développeur utilisait lambda et a introduit ce concept, rendant inutile la création de variables locales. Donc, si vous voyez que la règle pour la anonymous class n’est toujours pas modifiée, il vous suffit de ne pas écrire le mot clé final chaque fois que vous utilisez lambdas .

J’ai trouvé une bonne explication ici

Bien que d’autres réponses prouvent l’exigence, elles n’expliquent pas pourquoi l’exigence existe.

Le JLS mentionne pourquoi au § 15.27.2 :

La ressortingction pour les variables finales effectives interdit l’access aux variables locales à évolution dynamic, dont la capture introduirait probablement des problèmes de concurrence.

Pour réduire le risque de bogues, ils ont décidé de s’assurer que les variables capturées ne sont jamais mutées.

Dans votre exemple, vous pouvez remplacer le forEach par lamdba avec une simple boucle for et modifier n’importe quelle variable librement. Ou, probablement, refactorez votre code afin de ne modifier aucune variable. Cependant, je vous expliquerai, pour être complet, ce que signifie l’erreur et comment la contourner.

Spécification du langage Java 8, §15.27.2 :

Toute variable locale, paramètre formel ou paramètre d’exception utilisé mais non déclaré dans une expression lambda doit être déclaré final ou être effectivement final ( §4.12.4 ), ou une erreur de compilation se produit lorsque l’utilisation est tentée.

Fondamentalement, vous ne pouvez pas modifier une variable locale ( calTz dans ce cas) à partir d’un lambda (ou d’une classe locale / anonyme). Pour y parvenir en Java, vous devez utiliser un object mutable et le modifier (via une variable finale) à partir du lambda. Un exemple d’un object mutable serait un tableau d’un élément:

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

s’il n’est pas nécessaire de modifier la variable, une solution de contournement générale pour ce type de problème consisterait à extraire la partie de code qui utilise lambda et à utiliser le mot-clé final sur le paramètre de méthode.