final JTextField jtfContent = new JTextField(); btnOK.addActionListener(new java.awt.event.ActionListener(){ public void actionPerformed(java.awt.event.ActionEvent event){ jtfContent.setText("I am OK"); } } );
Si j’omets la final
, je vois l’erreur ” Ne peut pas se référer à une variable non finale jtfContent dans une classe interne définie dans une méthode différente “.
Pourquoi une classe interne anonyme doit-elle exiger que la variable d’instance de classes externes soit finale pour y accéder?
Bien, tout d’abord, détendons-nous, et s’il vous plaît, mettez cette arme à feu.
D’ACCORD. Maintenant, la raison pour laquelle le langage insiste là-dessus, c’est qu’il sortingche afin de fournir à vos fonctions de classe internes un access aux variables locales dont ils ont besoin. Le moteur d’exécution fait une copie du contexte d’exécution local (et ainsi de suite) et insiste donc pour que tout soit final
afin de garder les choses honnêtes.
Si cela n’a pas été fait, alors le code qui a changé la valeur d’une variable locale après que votre object ait été construit, mais avant que la fonction de classe interne ne s’exécute peut être déroutant et étrange.
C’est l’essence de beaucoup de brouhaha autour de Java et de “fermetures”.
Note: le paragraphe d’ouverture était une blague en référence à un texte en majuscule dans la composition originale du PO.
Les méthodes d’une classe anonyme n’ont pas vraiment access aux variables locales et aux parameters de méthode. Au contraire, lorsqu’un object de la classe anonyme est instancié, les copies des variables locales finales et des parameters de méthode auxquels font référence les méthodes de l’object sont stockées en tant que variables d’instance dans l’object. Les méthodes de l’object de la classe anonyme accèdent réellement à ces variables d’instance masquées. [1]
Ainsi, les variables locales et les parameters de méthode auxquels accèdent les méthodes de la classe locale doivent être déclarés finaux pour éviter que leurs valeurs ne changent après l’instanciation de l’object.
La raison en est que Java ne supporte pas complètement ce qu’on appelle les “fermetures” – dans ce cas la final
ne serait pas nécessaire – mais a trouvé une astuce en laissant le compilateur générer des variables cachées utilisées pour donner la fonctionnalité que vous voyez.
Si vous démontez le code d’octet généré, vous pouvez voir comment le compilateur le fait, y compris les variables cachées étrangement nommées contenant des copies des variables finales.
C’est une solution élégante pour donner des fonctionnalités sans plier le langage en arrière pour le faire.
Edit: Pour Java 8, les lambdas offrent un moyen plus concis de faire ce qui était fait auparavant avec les classes anonymes. Les ressortingctions sur les variables sont également passées de “final” à “essentiellement final” – vous n’avez pas à le déclarer final, mais s’il est traité comme s’il était final (vous pourriez append le mot clé final et le comstackr) peut être utilisé. C’est un changement vraiment sympa.
Les variables autour de la définition de la classe sont stockées dans la stack, elles ont donc probablement disparu lorsque le code de la classe interne est exécuté (si vous voulez savoir pourquoi, la stack de recherche et le segment de mémoire). C’est pourquoi les classes internes n’utilisent pas réellement les variables dans la méthode contenant, mais sont construites avec des copies de celles-ci.
Cela signifie que si vous modifiez la variable dans la méthode contenant après avoir construit la classe interne, sa valeur ne changera pas dans la classe interne, même si vous vous y attendez. Pour éviter toute confusion, Java exige qu’ils soient définitifs, vous ne pouvez donc pas les modifier.
Depuis Java 8, le modificateur final est facultatif pour les variables d’instance externes. La valeur devrait être «effectivement finale». Voir la réponse Différence entre finale et finale définitive .