Je joue avec des lambdas dans Java 8 et je suis tombé sur un avertissement local variables referenced from a lambda expression must be final or effectively final
. Je sais que lorsque j'utilise des variables à l'intérieur d'une classe anonyme, elles doivent être finales dans la classe externe, mais quand même - quelle est la différence entre final et effectivement final ?
351
Réponses:
Par exemple, supposons que la variable
numberLength
n'est pas déclarée finale et que vous ajoutez l'instruction d'affectation marquée dans lePhoneNumber
constructeur:En raison de cette instruction d'affectation, la variable numberLength n'est plus effectivement définitive. Par conséquent, le compilateur Java génère un message d'erreur similaire à «les variables locales référencées à partir d'une classe interne doivent être finales ou effectivement finales» où la classe interne PhoneNumber essaie d'accéder à la variable numberLength:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
la source
numberLength
devienne une variable locale de cette méthode.Je trouve que le moyen le plus simple d'expliquer "effectivement final" est d'imaginer ajouter le
final
modificateur à une déclaration de variable. Si, avec ce changement, le programme continue de se comporter de la même manière, à la fois au moment de la compilation et au moment de l'exécution, alors cette variable est effectivement définitive.la source
case k
nécessite une expression constante qui pourrait être une variable constante ("Une variable constante est une variable finale de type primitif ou de type String qui est initialisée avec une expression constante" JLS 4.12.4 ) qui est un cas particulier d'une finale variable.Selon les documents :
Fondamentalement, si le compilateur trouve qu'une variable n'apparaît pas dans les affectations en dehors de son initialisation, alors la variable est considérée comme effectivement finale .
Par exemple, considérez une classe:
la source
bar
dans votre exemple n'est pas une variable locale, mais un champ. "Effectivement final" dans le message d'erreur comme ci-dessus ne s'applique pas du tout aux champs.bar
est un paramètre ici, pas un champ.«Effectivement final» est une variable qui ne donnerait pas d'erreur de compilation si elle était ajoutée par «final»
D'après un article de «Brian Goetz»,
lambda-state-final- Brian Goetz
la source
Cette variable ci-dessous est finale , nous ne pouvons donc pas changer sa valeur une fois initialisée. Si nous essayons, nous obtiendrons une erreur de compilation ...
Mais si nous créons une variable comme celle-ci, nous pouvons changer sa valeur ...
Mais en Java 8 , toutes les variables sont finales par défaut. Mais l'existence de la 2ème ligne dans le code la rend non définitive . Donc, si nous supprimons la 2ème ligne du code ci-dessus, notre variable est désormais "effectivement définitive" ...
Donc .. Toute variable assignée une seule fois est "effectivement définitive" .
la source
Une variable est finale ou effectivement finale lorsqu'elle est initialisée une fois et qu'elle n'est jamais mutée dans sa classe propriétaire. Et nous ne pouvons pas l'initialiser en boucles ou en classes internes .
Finale :
Effectivement final :
la source
Lorsqu'une expression lambda utilise une variable locale affectée à partir de son espace englobant, il existe une restriction importante. Une expression lambda peut uniquement utiliser une variable locale dont la valeur ne change pas. Cette restriction est appelée " capture variable ", qui est décrite comme; l'expression lambda capture des valeurs, pas des variables .
Les variables locales qu'une expression lambda peut utiliser sont dites " effectivement finales ".
Une variable effectivement finale est une variable dont la valeur ne change pas après sa première affectation. Il n'est pas nécessaire de déclarer explicitement une telle variable comme finale, bien que cela ne soit pas une erreur.
Voyons-le avec un exemple, nous avons une variable locale i qui est initialisée avec la valeur 7, avec dans l'expression lambda nous essayons de changer cette valeur en assignant une nouvelle valeur à i. Cela entraînera une erreur du compilateur - « La variable locale i définie dans une étendue englobante doit être finale ou effectivement finale »
la source
Le sujet final efficace est décrit dans JLS 4.12.4 et le dernier paragraphe comprend une explication claire:
la source
final est une variable declare avec mot clé
final
, exemple:il reste tout au
final
long du programme.effectivement finale : toute variable ou paramètre local auquel une valeur n'est attribuée qu'une seule fois maintenant (ou mise à jour une seule fois). Il peut ne pas rester effectivement définitif long du programme. cela signifie donc que la variable effectivement finale peut perdre sa propriété effectivement finale après le moment où elle est affectée / mise à jour au moins une autre affectation. exemple:
la source
final
mot - clé à une déclaration sans introduire d'erreurs de compilation, alors ce n'est pas effectivement définitif . C'est le contraire de cette déclaration: "Si une variable est effectivement finale, l'ajout du dernier modificateur à sa déclaration n'introduira aucune erreur au moment de la compilation."Comme d'autres l'ont dit, une variable ou un paramètre dont la valeur n'est jamais modifiée après son initialisation est effectivement définitif. Dans le code ci-dessus, si vous modifiez la valeur de
x
dans la classe interne,FirstLevel
le compilateur vous donnera le message d'erreur:la source
Les expressions lambda peuvent accéder
variables statiques,
variables d'instance,
des paramètres de méthode finaux efficaces, et
variables locales finales efficaces.
Source: OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide, Jeanne Boyarsky, Scott Selikoff
Aditionellement,
Source: Commencer avec Java: des structures de contrôle aux objets (6e édition), Tony Gaddis
De plus, n'oubliez pas le sens de
final
ce qu'il est initialisé exactement une fois avant d'être utilisé pour la première fois.la source
Déclarer une variable
final
ou ne pas la déclarerfinal
, mais la garder effectivement finale peut entraîner (dépend du compilateur) un bytecode différent.Jetons un coup d'œil sur un petit exemple:
Le bytecode correspondant de la
main
méthode (Java 8u161 sur Windows 64 Bit):La table des numéros de ligne correspondante:
Comme on le voit le code source au niveau des lignes
12
,13
,14
ne figure pas dans le code d'octets. C'est parce quei
c'esttrue
et ne changera pas son état. Ainsi, ce code est inaccessible (plus dans cette réponse ). Pour la même raison, le code à la ligne9
manque également. Ili
n'est pas nécessaire d'évaluer l'état de car il esttrue
certain.D'un autre côté, bien que la variable
j
soit effectivement finale, elle n'est pas traitée de la même manière. Il n'y a pas de telles optimisations appliquées. L'état dej
est évalué deux fois. Le bytecode est le même, même s'ilj
est effectivement définitif .la source
La variable effectivement finale est une variable locale qui est:
final
Alors qu'une variable finale est une variable qui est:
final
mot clé.la source
Cela n'a pas commencé sur Java 8, je l'utilise depuis longtemps. Ce code utilisé (avant java 8) était légal:
la source
final
variables sont accessibles, mais en Java 8 également celles qui sont effectivement finales.