Bearbeitet: Ich muss die Werte mehrerer Variablen ändern, da sie mehrmals über einen Timer ausgeführt werden. Ich muss die Werte bei jeder Iteration durch den Timer aktualisieren. Ich kann die Werte nicht auf final setzen, da dies mich daran hindert, die Werte zu aktualisieren. Es wird jedoch der Fehler angezeigt, den ich in der ersten Frage unten beschrieben habe:
Ich hatte vorher geschrieben, was unten steht:
Ich erhalte die Fehlermeldung "kann nicht auf eine nicht endgültige Variable innerhalb einer inneren Klasse verweisen, die in einer anderen Methode definiert wurde".
Dies geschieht für den doppelten Preis und den Preis priceObject. Wissen Sie, warum ich dieses Problem bekomme? Ich verstehe nicht, warum ich eine endgültige Erklärung haben muss. Auch wenn Sie sehen können, was ich versuche zu tun, was muss ich tun, um dieses Problem zu umgehen.
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);
}
quelle
Antworten:
Java unterstützt keine echten Schließungen , obwohl die Verwendung einer anonymen Klasse wie hier (
new TimerTask() { ... }
) wie eine Art Schließung aussieht.Bearbeiten - Siehe die Kommentare unten - das Folgende ist keine korrekte Erklärung, wie KeeperOfTheSoul hervorhebt.
Deshalb funktioniert es nicht:
Die Variablen
lastPrice
und der Preis sind lokale Variablen in der main () -Methode. Das Objekt, das Sie mit der anonymen Klasse erstellen, kann bis zurmain()
Rückkehr der Methode gültig sein.Wenn die
main()
Methode zurückgegeben wird, werden lokale Variablen (wielastPrice
undprice
) aus dem Stapel bereinigt, sodass sie nach dermain()
Rückgabe nicht mehr vorhanden sind .Das anonyme Klassenobjekt verweist jedoch auf diese Variablen. Es würde furchtbar schief gehen, wenn das anonyme Klassenobjekt versucht, auf die Variablen zuzugreifen, nachdem sie bereinigt wurden.
Indem sie
lastPrice
und machenprice
final
, sind sie nicht mehr wirklich Variablen, sondern Konstanten. Der Compiler kann dann einfach die Verwendung vonlastPrice
undprice
in der anonymen Klasse durch die Werte der Konstanten ersetzen (natürlich zur Kompilierungszeit), und Sie haben kein Problem mehr mit dem Zugriff auf nicht vorhandene Variablen.Andere Programmiersprachen, die Closures unterstützen, behandeln diese Variablen speziell, indem sie sicherstellen, dass sie beim Beenden der Methode nicht zerstört werden, damit der Closure weiterhin auf die Variablen zugreifen kann.
@ Ankur: Du könntest das machen:
quelle
Um seltsame Nebenwirkungen bei Schließungen in Java-Variablen zu vermeiden, auf die von einem anonymen Delegaten verwiesen wird, muss sie als endgültig markiert werden. Um auf
lastPrice
die Timer-Aufgabe zu verweisen und sie zu bewerten, müssen sie als endgültig markiert werden.Dies funktioniert offensichtlich nicht für Sie, da Sie sie ändern möchten. In diesem Fall sollten Sie versuchen, sie in einer Klasse zu kapseln.
Jetzt erstelle einfach ein neues Foo als final und rufe .tick vom Timer auf.
quelle
Sie können nur dann auf endgültige Variablen aus der enthaltenen Klasse zugreifen, wenn Sie eine anonyme Klasse verwenden. Daher müssen Sie die verwendeten Variablen als final deklarieren (was für Sie keine Option ist, da Sie lastPrice und price ändern ) oder keine anonyme Klasse verwenden.
Sie können also eine tatsächliche innere Klasse erstellen, in der Sie die Variablen übergeben und auf normale Weise verwenden können
oder:
Es gibt einen schnellen (und meiner Meinung nach hässlichen) Hack für Ihre letzte Preis- und Preisvariable , der darin besteht, dies so zu deklarieren
und in Ihrer anonymen Klasse können Sie den Wert so einstellen
quelle
Gute Erklärungen dafür, warum Sie nicht das tun können, was Sie versuchen, bereits bereitgestellt. Betrachten Sie als Lösung vielleicht:
Scheint, als könnten Sie wahrscheinlich ein besseres Design als das machen, aber die Idee ist, dass Sie die aktualisierten Variablen in einer Klassenreferenz gruppieren könnten, die sich nicht ändert.
quelle
Bei anonymen Klassen deklarieren Sie tatsächlich eine "namenlose" verschachtelte Klasse. Für verschachtelte Klassen generiert der Compiler eine neue eigenständige öffentliche Klasse mit einem Konstruktor, der alle von ihm verwendeten Variablen als Argumente verwendet (für "benannte" verschachtelte Klassen ist dies immer eine Instanz der ursprünglichen / umschließenden Klasse). Dies geschieht, weil die Laufzeitumgebung keine Vorstellung von verschachtelten Klassen hat. Daher muss eine (automatische) Konvertierung von einer verschachtelten in eine eigenständige Klasse erfolgen.
Nehmen Sie diesen Code zum Beispiel:
Das wird nicht funktionieren, denn das macht der Compiler unter der Haube:
Die ursprüngliche anonyme Klasse wird durch eine eigenständige Klasse ersetzt, die der Compiler generiert (Code ist nicht genau, sollte Ihnen aber eine gute Idee geben):
Wie Sie sehen können, enthält die eigenständige Klasse einen Verweis auf das gemeinsam genutzte Objekt. Denken Sie daran, dass alles in Java als Wert übergeben wird. Selbst wenn die Referenzvariable 'shared' in EnclosingClass geändert wird, wird die Instanz, auf die sie verweist, nicht geändert und alle anderen Referenzvariablen, die darauf verweisen (wie die in der anonymen Klasse: Enclosing $ 1), werden dies nicht bemerken. Dies ist der Hauptgrund, warum der Compiler Sie zwingt, diese "gemeinsam genutzten" Variablen als endgültig zu deklarieren, damit diese Art von Verhalten nicht in Ihren bereits ausgeführten Code gelangt.
Dies geschieht nun, wenn Sie eine Instanzvariable in einer anonymen Klasse verwenden (dies sollten Sie tun, um Ihr Problem zu lösen, Ihre Logik auf eine "Instanz" -Methode oder einen Konstruktor einer Klasse zu verschieben):
Dies lässt sich gut kompilieren, da der Compiler den Code so ändert, dass die neu generierte Klasse Enclosing $ 1 einen Verweis auf die Instanz von EnclosingClass enthält, in der sie instanziiert wurde (dies ist nur eine Darstellung, sollte Sie aber zum Laufen bringen):
Wenn die Referenzvariable 'shared' in EnclosingClass neu zugewiesen wird und dies vor dem Aufruf von Thread # run () geschieht, wird "other hello" zweimal gedruckt, da jetzt die EnclosingClass $ 1 # -Einschließungsvariable eine Referenz behält für das Objekt der Klasse, in der es deklariert wurde, sodass Änderungen an einem Attribut für dieses Objekt für Instanzen von EnclosingClass $ 1 sichtbar sind.
Weitere Informationen zu diesem Thema finden Sie in diesem hervorragenden Blog-Beitrag (nicht von mir verfasst): http://kevinboone.net/java_inner.html
quelle
Wenn ich auf dieses Problem stoße, übergebe ich die Objekte einfach über den Konstruktor an die innere Klasse. Wenn ich Grundelemente oder unveränderliche Objekte übergeben muss (wie in diesem Fall), wird eine Wrapper-Klasse benötigt.
Bearbeiten: Eigentlich verwende ich überhaupt keine anonyme Klasse, sondern eine richtige Unterklasse:
quelle
Sie können nicht auf nicht endgültige Variablen verweisen, da dies in der Java-Sprachspezifikation angegeben ist. Ab 8.1.3:
"Alle lokalen Variablen, formalen Methodenparameter oder Ausnahmebehandlungsparameter, die in einer inneren Klasse verwendet, aber nicht deklariert werden, müssen als endgültig deklariert werden." Ganzer Absatz.
Ich kann nur einen Teil Ihres Codes sehen - meiner Meinung nach ist es eine seltsame Idee, Änderungen an lokalen Variablen zu planen. Lokale Variablen existieren nicht mehr, wenn Sie die Funktion verlassen. Vielleicht wären statische Felder einer Klasse besser?
quelle
Ich habe gerade etwas geschrieben, um etwas in der Absicht des Autors zu handhaben . Ich fand es am besten, den Konstruktor alle Objekte nehmen zu lassen und dann in Ihrer implementierten Methode diese Konstruktorobjekte zu verwenden.
Wenn Sie jedoch eine generische Schnittstellenklasse schreiben, müssen Sie ein Objekt oder besser eine Liste von Objekten übergeben. Dies könnte mit Object [] oder noch besser mit Object ... geschehen, da es einfacher ist, es aufzurufen.
Siehe mein Beispiel unten.
In diesem Beitrag finden Sie Informationen zu Java-Schließungen, die dies sofort unterstützen: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
Version 1 unterstützt das Übergeben nicht endgültiger Schließungen mit Autocasting:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
quelle
Wenn Sie einen Wert in einem Methodenaufruf innerhalb einer anonymen Klasse ändern möchten, ist dieser "Wert" tatsächlich ein
Future
. Wenn Sie also Guava verwenden, können Sie schreibenquelle
Eine Lösung, die mir aufgefallen ist, wird nicht erwähnt (es sei denn, ich habe sie verpasst, wenn ich mich korrigiert habe), ist die Verwendung einer Klassenvariablen. Beim Versuch, einen neuen Thread innerhalb einer Methode auszuführen, ist dieses Problem aufgetreten :
new Thread(){ Do Something }
.Das Anrufen
doSomething()
über Folgendes funktioniert. Sie müssen es nicht unbedingt deklarieren, sondernfinal
müssen nur den Umfang der Variablen ändern, damit sie nicht vor der inneren Klasse erfasst wird. Dies gilt nur, wenn Ihr Prozess natürlich sehr umfangreich ist und eine Änderung des Bereichs zu Konflikten führen kann. Ich wollte meine Variable nicht endgültig machen, da sie in keiner Weise endgültig / konstant war.quelle
Wenn die Variable, die endgültig sein muss, nicht endgültig sein kann, können Sie den Wert der Variablen einer anderen Variablen zuweisen und DIESE endgültig machen, damit Sie sie stattdessen verwenden können.
quelle
Verwenden Sie ClassName.this.variableName, um auf die nicht endgültige Variable zu verweisen
quelle
Sie können die Variable einfach außerhalb der äußeren Klasse deklarieren. Danach können Sie die Variable innerhalb der inneren Klasse bearbeiten. Ich habe manchmal ähnliche Probleme beim Codieren in Android, daher deklariere ich die Variable als global und es funktioniert für mich.
quelle
Können Sie machen
lastPrice
,priceObject
undprice
Felder der anonymen inneren Klasse?quelle
Die Hauptsorge ist, ob eine Variable innerhalb der anonymen Klasseninstanz zur Laufzeit aufgelöst werden kann. Es ist kein Muss, eine Variable endgültig zu machen, solange garantiert ist, dass sich die Variable innerhalb des Laufzeitbereichs befindet. Sehen Sie sich beispielsweise die beiden Variablen _statusMessage und _statusTextView in der updateStatus () -Methode an.
quelle
Was für mich funktioniert hat, ist nur die Variable außerhalb dieser Funktion von Ihnen zu definieren.
Kurz vor der Hauptfunktion deklarieren dh
quelle
Deklarieren Sie die Variable als statisch und referenzieren Sie sie in der erforderlichen Methode mit className.variable
quelle
Non-static parameter cannot be referenced from a static context
Nur eine andere Erklärung. Betrachten Sie dieses Beispiel unten
Hier wird die Ausgabe sein
m1 wird abgeschlossen
Thread t läuft
Thread t läuft
Thread t läuft
................
Jetzt ist die Methode m1 () abgeschlossen und wir weisen null die Referenzvariable o zu. Jetzt ist das äußere Klassenobjekt für die GC berechtigt, aber das innere Klassenobjekt ist noch vorhanden, das eine (Has-A) Beziehung zum laufenden Thread-Objekt hat. Ohne vorhandenes Outer-Class-Objekt gibt es keine Chance für eine vorhandene m1 () -Methode und ohne vorhandene m1 () -Methode gibt es keine Chance für eine vorhandene lokale Variable. Wenn Inner Class Object jedoch die lokale Variable der m1 () -Methode verwendet, ist alles selbsterklärend .
Um dies zu lösen, müssen wir eine Kopie der lokalen Variablen erstellen und dann mit dem Objekt der inneren Klasse in den Heap kopieren, was Java nur für die endgültige Variable tut, da sie nicht wirklich variabel sind, sondern wie Konstanten (alles geschieht nur zur Kompilierungszeit) nicht zur Laufzeit).
quelle
Um das oben genannte Problem zu lösen, treffen verschiedene Sprachen unterschiedliche Entscheidungen.
Für Java entspricht die Lösung dem, was wir in diesem Artikel sehen.
Für C # besteht die Lösung darin, Nebenwirkungen zuzulassen, und die Erfassung als Referenz ist die einzige Option.
Für C ++ 11 besteht die Lösung darin, dem Programmierer die Entscheidung zu ermöglichen. Sie können wählen, ob sie nach Wert oder nach Referenz erfassen möchten. Bei der Erfassung nach Wert würden keine Nebenwirkungen auftreten, da die referenzierte Variable tatsächlich unterschiedlich ist. Bei der Erfassung als Referenz können Nebenwirkungen auftreten, die der Programmierer jedoch erkennen sollte.
quelle
Weil es verwirrend ist, wenn die Variable nicht endgültig ist, da die Änderungen nicht in der anonymen Klasse übernommen werden.
Machen Sie einfach die Variablen 'price' und 'lastPrice' endgültig.
- Bearbeiten
Hoppla, und Sie müssen sie natürlich auch in Ihrer Funktion nicht zuweisen. Sie benötigen neue lokale Variablen. Wie auch immer, ich vermute, jemand hat Ihnen inzwischen eine bessere Antwort gegeben.
quelle