a
kann hier nur endgültig sein. Warum? Wie kann ich neu zuweisena
inonClick()
Verfahren ohne es als privat Mitglied zu halten?private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); }
Wie kann ich das zurückgeben,
5 * a
wenn es geklickt hat? Ich meine,private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }
java
event-handling
anonymous-class
Andrii Abramov
quelle
quelle
Antworten:
Wie in den Kommentaren erwähnt, ist ein Teil davon in Java 8 irrelevant, wo
final
dies implizit sein kann. In einer anonymen inneren Klasse oder einem Lambda-Ausdruck kann jedoch nur eine effektiv endgültige Variable verwendet werden.Dies liegt im Wesentlichen an der Art und Weise, wie Java Schließungen verwaltet .
Wenn Sie eine Instanz einer anonymen inneren Klasse erstellen, werden die Werte aller Variablen, die in dieser Klasse verwendet werden, über den automatisch generierten Konstruktor kopiert. Dies vermeidet, dass der Compiler verschiedene zusätzliche Typen automatisch generieren muss, um den logischen Status der "lokalen Variablen" zu speichern, wie dies beispielsweise der C # -Compiler tut ... (Wenn C # eine Variable in einer anonymen Funktion erfasst, erfasst es die Variable - die Das Schließen kann die Variable auf eine Weise aktualisieren, die vom Hauptteil der Methode gesehen wird, und umgekehrt.)
Da der Wert in die Instanz der anonymen inneren Klasse kopiert wurde, würde es seltsam aussehen, wenn die Variable durch den Rest der Methode geändert werden könnte - Sie könnten Code haben, der anscheinend mit einer veralteten Variablen funktioniert ( denn genau das würde passieren ... Sie würden mit einer Kopie arbeiten, die zu einem anderen Zeitpunkt aufgenommen wurde. Wenn Sie Änderungen innerhalb der anonymen inneren Klasse vornehmen könnten, könnten Entwickler erwarten, dass diese Änderungen im Hauptteil der einschließenden Methode sichtbar sind.
Wenn Sie die Variable final machen, werden alle diese Möglichkeiten entfernt. Da der Wert überhaupt nicht geändert werden kann, müssen Sie sich keine Gedanken darüber machen, ob solche Änderungen sichtbar sind. Die einzige Möglichkeit, der Methode und der anonymen inneren Klasse zu erlauben, die Änderungen des anderen zu sehen, besteht darin, einen veränderlichen Typ einer Beschreibung zu verwenden. Dies könnte die einschließende Klasse selbst sein, ein Array, ein veränderbarer Wrapper-Typ ... so etwas. Im Grunde ist es ein bisschen wie die Kommunikation zwischen einer Methode und einer anderen: Änderungen, die an den Parametern einer Methode vorgenommen wurden, werden vom Aufrufer nicht gesehen, aber Änderungen an den Objekten, auf die die Parameter verweisen, werden angezeigt.
Wenn Sie an einem detaillierteren Vergleich zwischen Java- und C # -Verschlüssen interessiert sind, habe ich einen Artikel, der weiter darauf eingeht. Ich wollte mich in dieser Antwort auf die Java-Seite konzentrieren :)
quelle
final
(aber er muss noch effektiv endgültig sein).Es gibt einen Trick, mit dem anonyme Klassen Daten im äußeren Bereich aktualisieren können.
Dieser Trick ist jedoch aufgrund der Synchronisationsprobleme nicht sehr gut. Wenn der Handler später aufgerufen wird, müssen Sie 1) den Zugriff auf res synchronisieren, wenn der Handler vom anderen Thread aufgerufen wurde. 2) eine Art Flag oder Anzeige haben, dass res aktualisiert wurde
Dieser Trick funktioniert jedoch einwandfrei, wenn eine anonyme Klasse sofort im selben Thread aufgerufen wird. Mögen:
quelle
List.forEach
Code sicher ist.Eine anonyme Klasse ist eine innere Klasse und die strenge Regel gilt für innere Klassen (JLS 8.1.3) :
Ich habe noch keinen Grund oder eine Erklärung für die jls oder jvms gefunden, aber wir wissen, dass der Compiler für jede innere Klasse eine separate Klassendatei erstellt und sicherstellen muss, dass die in dieser Klassendatei deklarierten Methoden ( auf Bytecode-Ebene) mindestens Zugriff auf die Werte lokaler Variablen haben.
( Jon hat die vollständige Antwort - ich halte diese ungelöscht, weil man an der JLS-Regel interessiert sein könnte)
quelle
Sie können eine Variable auf Klassenebene erstellen, um den zurückgegebenen Wert abzurufen. ich meine
Jetzt können Sie den Wert von K erhalten und ihn verwenden, wo Sie möchten.
Die Antwort auf Ihr Warum lautet:
Eine lokale Instanz der inneren Klasse ist an die Hauptklasse gebunden und kann auf die endgültigen lokalen Variablen ihrer enthaltenen Methode zugreifen. Wenn die Instanz ein endgültiges lokales Element ihrer enthaltenen Methode verwendet, behält die Variable den Wert bei, den sie zum Zeitpunkt der Erstellung der Instanz hatte, auch wenn die Variable den Gültigkeitsbereich verlassen hat (dies ist effektiv Javas grobe, begrenzte Version von Schließungen).
Da eine lokale innere Klasse weder Mitglied einer Klasse noch eines Pakets ist, wird sie nicht mit einer Zugriffsebene deklariert. (Stellen Sie jedoch klar, dass die eigenen Mitglieder Zugriffsebenen wie in einer normalen Klasse haben.)
quelle
Nun, in Java kann eine Variable nicht nur als Parameter endgültig sein, sondern auch als Feld auf Klassenebene
oder als lokale Variable, wie
Wenn Sie von einer anonymen Klasse aus auf eine Variable zugreifen und diese ändern möchten, möchten Sie die Variable möglicherweise zu einer Variablen auf Klassenebene in der umschließenden Klasse machen.
Sie können eine Variable nicht als endgültig festlegen und ihr einen neuen Wert geben.
final
bedeutet genau das: Der Wert ist unveränderlich und endgültig.Und da es endgültig ist, kann Java es sicher in lokale anonyme Klassen kopieren . Sie erhalten keinen Verweis auf das int (insbesondere, da Sie in Java keine Verweise auf Grundelemente wie int haben können, sondern nur Verweise auf Objekte ).
Es kopiert einfach den Wert von a in ein implizites int, das in Ihrer anonymen Klasse als a bezeichnet wird.
quelle
static
. Vielleicht ist es klarer, wenn Sie stattdessen "Instanzvariable" verwenden.Der Grund, warum der Zugriff nur auf die lokalen endgültigen Variablen beschränkt wurde, besteht darin, dass, wenn alle lokalen Variablen zugänglich gemacht würden, sie zuerst in einen separaten Abschnitt kopiert werden müssten, in dem innere Klassen Zugriff auf sie haben und mehrere Kopien von verwalten könnten veränderbare lokale Variablen können zu inkonsistenten Daten führen. Während endgültige Variablen unveränderlich sind und daher eine beliebige Anzahl von Kopien keine Auswirkungen auf die Datenkonsistenz hat.
quelle
Int
innerhalb eines Abschlusses neu zugewiesen wird, ändern Sie diese Variable in eine Instanz vonIntRef
(im Wesentlichen einen veränderlichenInteger
Wrapper). Jeder Variablenzugriff wird dann entsprechend umgeschrieben.Betrachten Sie das folgende Programm, um die Gründe für diese Einschränkung zu verstehen:
Die interfaceInstance bleibt nach der Rückkehr der Initialisierungsmethode im Speicher , der Parameter val jedoch nicht. Die JVM kann nicht auf eine lokale Variable außerhalb ihres Gültigkeitsbereichs zugreifen. Daher führt Java den nachfolgenden Aufruf von printInteger aus , indem der Wert von val in ein implizites Feld mit demselben Namen in interfaceInstance kopiert wird . Die interfaceInstance soll den Wert des lokalen Parameters erfasst haben . Wenn der Parameter nicht endgültig (oder effektiv endgültig) wäre, könnte sich sein Wert ändern und nicht mehr mit dem erfassten Wert synchronisiert werden, was möglicherweise zu einem nicht intuitiven Verhalten führen könnte.
quelle
Methoden innerhalb einer anonomischen inneren Klasse können gut aufgerufen werden, nachdem der Thread, der sie erzeugt hat, beendet wurde. In Ihrem Beispiel wird die innere Klasse im Ereignisversand-Thread aufgerufen und nicht im selben Thread wie der, der sie erstellt hat. Daher ist der Umfang der Variablen unterschiedlich. Um solche Probleme mit dem Variablenzuweisungsbereich zu schützen, müssen Sie sie für endgültig erklären.
quelle
Wenn eine anonyme innere Klasse im Hauptteil einer Methode definiert ist, kann auf alle im Bereich dieser Methode als endgültig deklarierten Variablen innerhalb der inneren Klasse zugegriffen werden. Bei skalaren Werten kann sich der Wert der endgültigen Variablen nach der Zuweisung nicht mehr ändern. Bei Objektwerten kann sich die Referenz nicht ändern. Auf diese Weise kann der Java-Compiler den Wert der Variablen zur Laufzeit "erfassen" und eine Kopie als Feld in der inneren Klasse speichern. Sobald die äußere Methode beendet und ihr Stapelrahmen entfernt wurde, ist die ursprüngliche Variable verschwunden, aber die private Kopie der inneren Klasse bleibt im eigenen Speicher der Klasse erhalten.
( http://en.wikipedia.org/wiki/Final_%28Java%29 )
quelle
quelle
Da Jon die Implementierungsdetails beantwortet hat, wäre eine andere mögliche Antwort, dass die JVM das Schreiben von Datensätzen, die seine Aktivierung beendet haben, nicht verarbeiten möchte.
Stellen Sie sich den Anwendungsfall vor, in dem Ihre Lambdas nicht angewendet, sondern an einem Ort gespeichert und später ausgeführt werden.
Ich erinnere mich, dass Sie in Smalltalk ein illegales Geschäft eröffnen würden, wenn Sie solche Änderungen vornehmen.
quelle
Versuchen Sie diesen Code,
Erstellen Sie eine Array-Liste, geben Sie einen Wert ein und geben Sie ihn zurück:
quelle
Die anonyme Java-Klasse ist dem Schließen von Javascript sehr ähnlich, aber Java implementiert dies auf andere Weise. (Überprüfen Sie Andersens Antwort)
Um den Java-Entwickler nicht mit dem seltsamen Verhalten zu verwechseln, das bei Personen mit Javascript-Hintergrund auftreten kann. Ich denke, deshalb zwingen sie uns
final
, dies zu verwenden . Dies ist nicht die JVM-Einschränkung.Schauen wir uns das folgende Javascript-Beispiel an:
In Javascript ist der
counter
Wert 100, da escounter
von Anfang bis Ende nur eine Variable gibt .Wenn dies in Java nicht der Fall ist
final
, wird es ausgedruckt0
, da der0
Wert während der Erstellung des inneren Objekts in die verborgenen Eigenschaften des Objekts der inneren Klasse kopiert wird. (Hier gibt es zwei ganzzahlige Variablen, eine in der lokalen Methode und eine in den versteckten Eigenschaften der inneren Klasse.)Änderungen nach der Erstellung des inneren Objekts (wie Zeile 1) wirken sich also nicht auf das innere Objekt aus. Dies führt zu Verwechslungen zwischen zwei unterschiedlichen Ergebnissen und Verhaltensweisen (zwischen Java und Javascript).
Ich glaube, aus diesem Grund beschließt Java, die endgültige Festlegung zu erzwingen, sodass die Daten von Anfang bis Ende "konsistent" sind.
quelle
Java letzte Variable innerhalb einer inneren Klasse
innere Klasse kann nur verwenden
Object
...)int
...) Typ kann gewickelt durch einen endgültigen Referenztyp.IntelliJ IDEA
kann Ihnen helfen, es in ein Elementarray umzuwandelnWenn ein
non static nested
(inner class
) [Info] vom Compiler generiert wird - eine neue Klasse -<OuterClass>$<InnerClass>.class
wird erstellt und gebundene Parameter an den Konstruktor [Lokale Variable auf Stapel] übergeben . Es ist ähnlich wie bei der SchließungDie endgültige Variable ist eine Variable, die nicht neu zugewiesen werden kann. Die endgültige Referenzvariable kann weiterhin durch Ändern eines Status geändert werden
Es war möglich, dass es komisch wäre, weil man als Programmierer so etwas machen könnte
quelle
Vielleicht gibt dir dieser Trick eine Idee
quelle