Modifié: j'ai besoin de modifier les valeurs de plusieurs variables car elles s'exécutent plusieurs fois par minuterie. Je dois continuer à mettre à jour les valeurs à chaque itération du minuteur. Je ne peux pas définir les valeurs sur final car cela m'empêchera de mettre à jour les valeurs mais je reçois l'erreur que je décris dans la question initiale ci-dessous:
J'avais précédemment écrit ce qui est ci-dessous:
J'obtiens l'erreur "ne peut pas faire référence à une variable non finale à l'intérieur d'une classe interne définie dans une méthode différente".
Cela se produit pour le double appelé prix et le prix appelé priceObject. Savez-vous pourquoi j'ai ce problème. Je ne comprends pas pourquoi j'ai besoin d'une déclaration finale. De plus, si vous pouvez voir ce que j'essaie de faire, que dois-je faire pour contourner ce problème.
public static void main(String args[]) {
int period = 2000;
int delay = 2000;
double lastPrice = 0;
Price priceObject = new Price();
double price = 0;
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
price = priceObject.getNextPrice(lastPrice);
System.out.println();
lastPrice = price;
}
}, delay, period);
}
la source
Réponses:
Java ne prend pas en charge les vraies fermetures , même si l'utilisation d'une classe anonyme comme celle que vous utilisez ici (
new TimerTask() { ... }
) ressemble à une sorte de fermeture.modifier - Voir les commentaires ci-dessous - ce qui suit n'est pas une explication correcte, comme le souligne KeeperOfTheSoul.
C'est pourquoi cela ne fonctionne pas:
Les variables
lastPrice
et le prix sont des variables locales dans la méthode main (). L'objet que vous créez avec la classe anonyme peut durer jusqu'au retour de lamain()
méthode.Lorsque la
main()
méthode revient, les variables locales (telles quelastPrice
etprice
) seront nettoyées de la pile, elles n'existeront donc plus après lesmain()
retours.Mais l'objet de classe anonyme fait référence à ces variables. Les choses iraient terriblement mal si l'objet de classe anonyme essayait d'accéder aux variables après leur nettoyage.
En faisant
lastPrice
etprice
final
, ce ne sont plus vraiment des variables, mais des constantes. Le compilateur peut alors simplement remplacer l'utilisation delastPrice
etprice
dans la classe anonyme par les valeurs des constantes (au moment de la compilation, bien sûr), et vous n'aurez plus le problème d'accéder à des variables inexistantes.D'autres langages de programmation qui prennent en charge les fermetures le font en traitant ces variables spécialement - en s'assurant qu'elles ne sont pas détruites à la fin de la méthode, afin que la fermeture puisse toujours accéder aux variables.
@Ankur: Vous pouvez le faire:
la source
Pour éviter des effets secondaires étranges avec des fermetures dans les variables java référencées par un délégué anonyme,
lastPrice
elles doivent être marquées comme finales.Cela ne fonctionnera évidemment pas pour vous parce que vous souhaitez les changer, dans ce cas, vous devriez envisager de les encapsuler dans une classe.
il suffit maintenant de créer un nouveau Foo en tant que final et d'appeler .tick depuis la minuterie.
la source
Vous ne pouvez accéder aux variables finales qu'à partir de la classe conteneur lorsque vous utilisez une classe anonyme. Par conséquent, vous devez déclarer les variables utilisées comme finales (ce qui n'est pas une option pour vous puisque vous modifiez lastPrice et price ), ou n'utilisez pas de classe anonyme.
Vos options sont donc de créer une classe interne réelle, dans laquelle vous pouvez passer les variables et les utiliser de manière normale
ou:
Il y a un hack rapide (et à mon avis moche) pour votre dernière variable de prix et de prix qui est de le déclarer ainsi
et dans votre classe anonyme, vous pouvez définir la valeur comme ceci
la source
De bonnes explications pour lesquelles vous ne pouvez pas faire ce que vous essayez de faire déjà fournies. Comme solution, envisagez peut-être:
Il semble que vous pourriez probablement faire une meilleure conception que cela, mais l'idée est que vous pouvez regrouper les variables mises à jour dans une référence de classe qui ne change pas.
la source
Avec les classes anonymes, vous déclarez en fait une classe imbriquée "sans nom". Pour les classes imbriquées, le compilateur génère une nouvelle classe publique autonome avec un constructeur qui prendra toutes les variables qu'il utilise comme arguments (pour les classes imbriquées "nommées", il s'agit toujours d'une instance de la classe d'origine / englobante). Cela est dû au fait que l'environnement d'exécution n'a aucune notion de classes imbriquées, il doit donc y avoir une conversion (automatique) d'une classe imbriquée vers une classe autonome.
Prenez ce code par exemple:
Cela ne fonctionnera pas, car c'est ce que le compilateur fait sous le capot:
La classe anonyme d'origine est remplacée par une classe autonome générée par le compilateur (le code n'est pas exact, mais devrait vous donner une bonne idée):
Comme vous pouvez le voir, la classe autonome contient une référence à l'objet partagé, rappelez-vous que tout en java est une valeur de passage, donc même si la variable de référence 'shared' dans EnclosingClass est modifiée, l'instance vers laquelle elle pointe n'est pas modifiée , et toutes les autres variables de référence pointant vers lui (comme celle de la classe anonyme: Enclosing $ 1), n'en seront pas conscientes. C'est la raison principale pour laquelle le compilateur vous oblige à déclarer ces variables «partagées» comme finales, afin que ce type de comportement ne soit pas intégré dans votre code déjà en cours d'exécution.
Maintenant, c'est ce qui se passe lorsque vous utilisez une variable d'instance à l'intérieur d'une classe anonyme (c'est ce que vous devez faire pour résoudre votre problème, déplacez votre logique vers une méthode "instance" ou un constructeur d'une classe):
Cela compile très bien, car le compilateur modifiera le code, de sorte que la nouvelle classe générée Enclosing $ 1 contiendra une référence à l'instance de EnclosingClass où elle a été instanciée (ce n'est qu'une représentation, mais devrait vous permettre de continuer):
Comme ceci, lorsque la variable de référence «partagée» dans EnclosingClass est réaffectée, et cela se produit avant l'appel à Thread # run (), vous verrez «autre bonjour» imprimé deux fois, parce que maintenant la variable entourante EnclosingClass $ 1 # gardera une référence à l'objet de la classe où il a été déclaré, les modifications apportées à tout attribut de cet objet seront donc visibles par les instances de EnclosingClass $ 1.
Pour plus d'informations sur le sujet, vous pouvez voir cet excellent article de blog (non écrit par moi): http://kevinboone.net/java_inner.html
la source
Lorsque je tombe sur ce problème, je passe simplement les objets à la classe interne via le constructeur. Si j'ai besoin de passer des primitives ou des objets immuables (comme dans ce cas), une classe wrapper est nécessaire.
Edit: En fait, je n'utilise pas du tout une classe anonyme, mais une sous-classe appropriée:
la source
Vous ne pouvez pas faire référence à des variables non finales car la spécification du langage Java le dit. De 8.1.3:
"Toute variable locale, paramètre de méthode formelle ou paramètre de gestionnaire d'exception utilisé mais non déclaré dans une classe interne doit être déclaré final." Paragraphe entier.
Je ne peux voir qu'une partie de votre code - selon moi, la planification de la modification des variables locales est une idée étrange. Les variables locales cessent d'exister lorsque vous quittez la fonction. Peut-être que les champs statiques d'une classe seraient mieux?
la source
Je viens d'écrire quelque chose pour gérer quelque chose selon l' intention des auteurs . J'ai trouvé que la meilleure chose à faire était de laisser le constructeur prendre tous les objets et ensuite dans votre méthode implémentée utiliser ces objets constructeur.
Cependant, si vous écrivez une classe d'interface générique, vous devez passer un objet, ou mieux une liste d'objets. Cela pourrait être fait par Object [] ou encore mieux, Object ... car il est plus facile d'appeler.
Voir mon exemple de pièce juste en dessous.
Veuillez consulter cet article sur les fermetures Java qui prend en charge cela hors de la boîte: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
La version 1 prend en charge le passage des fermetures non définitives avec la diffusion automatique:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
la source
Si vous souhaitez modifier une valeur dans un appel de méthode au sein d'une classe anonyme, cette "valeur" est en fait un
Future
. Donc, si vous utilisez Guava, vous pouvez écrirela source
Une solution que j'ai remarquée n'est pas mentionnée (sauf si je l'ai manquée, si je la corrige s'il vous plaît), est l'utilisation d'une variable de classe. Ran dans cette question essayant de lancer un nouveau thread dans une méthode:
new Thread(){ Do Something }
.L'appel
doSomething()
de ce qui suit fonctionnera. Vous n'avez pas nécessairement à le déclarer, ilfinal
suffit de changer la portée de la variable pour qu'elle ne soit pas collectée avant la classe interne. C'est à moins bien sûr que votre processus soit énorme et que le changement de portée puisse créer une sorte de conflit. Je ne voulais pas rendre ma variable finale car ce n'était en aucun cas une finale / constante.la source
Si la variable requise pour être finale ne peut pas l'être, vous pouvez attribuer la valeur de la variable à une autre variable et la rendre finale pour pouvoir l'utiliser à la place.
la source
utilisez ClassName.this.variableName pour référencer la variable non finale
la source
vous pouvez simplement déclarer la variable en dehors de la classe externe. Après cela, vous pourrez modifier la variable à partir de la classe interne. Je fais parfois face à des problèmes similaires lors du codage dans Android, donc je déclare la variable comme globale et cela fonctionne pour moi.
la source
Pouvez - vous faire
lastPrice
,priceObject
et lesprice
champs de la classe interne anonyme?la source
La principale préoccupation est de savoir si une variable à l'intérieur de l'instance de classe anonyme peut être résolue au moment de l'exécution. Il n'est pas indispensable de rendre une variable finale tant qu'il est garanti que la variable se trouve dans la portée d'exécution. Par exemple, consultez les deux variables _statusMessage et _statusTextView dans la méthode updateStatus ().
la source
ce qui a fonctionné pour moi est simplement de définir la variable en dehors de cette fonction de votre.
Juste avant la fonction principale, déclarer ie
la source
Déclarez la variable comme statique et référencez-la dans la méthode requise en utilisant className.variable
la source
Non-static parameter cannot be referenced from a static context
Juste une autre explication. Considérez cet exemple ci-dessous
Ici, la sortie sera
m1 termine
Thread t running
Thread t running
Thread t running
................
Maintenant, la méthode m1 () se termine et nous affectons la variable de référence o à null, maintenant l'objet de classe externe est éligible pour GC mais l'objet de classe interne existe toujours qui a une relation (Has-A) avec l'objet Thread en cours d'exécution. Sans objet de classe externe existant, il n'y a aucune chance que la méthode m1 () existe et sans méthode m1 (), il n'y a aucune chance d'exister sa variable locale, mais si l'objet de classe interne utilise la variable locale de la méthode m1 (), alors tout s'explique de lui-même. .
Pour résoudre ce problème, nous devons créer une copie de la variable locale, puis copier dans le tas avec l'objet de classe Inner, ce que Java fait pour la seule variable finale, car ils ne sont pas réellement variables, ils sont comme des constantes (tout se passe au moment de la compilation uniquement pas à l'exécution).
la source
Pour résoudre le problème ci-dessus, différentes langues prennent des décisions différentes.
pour Java, la solution est celle que nous voyons dans cet article.
pour C #, la solution est d'autoriser les effets secondaires et la capture par référence est la seule option.
pour C ++ 11, la solution est de permettre au programmeur de prendre la décision. Ils peuvent choisir de capturer par valeur ou par référence. En cas de capture par valeur, aucun effet secondaire ne se produirait car la variable référencée est en fait différente. Si la capture par référence, des effets secondaires peuvent se produire, mais le programmeur doit s'en rendre compte.
la source
Parce que c'est déroutant si la variable n'est pas définitive, car les modifications ne seront pas récupérées dans la classe anonyme.
Rendez les variables 'prix' et 'lastPrice' finales.
-- Éditer
Oups, et vous devrez également ne pas leur affecter, évidemment, dans votre fonction. Vous aurez besoin de nouvelles variables locales. Quoi qu'il en soit, je soupçonne que quelqu'un vous a déjà donné une meilleure réponse.
la source