Ich spiele mit Lambdas in Java 8 und bin auf eine Warnung gestoßen local variables referenced from a lambda expression must be final or effectively final
. Ich weiß, dass Variablen, die in einer anonymen Klasse verwendet werden, in der äußeren Klasse endgültig sein müssen, aber dennoch - was ist der Unterschied zwischen endgültig und effektiv endgültig ?
350
Antworten:
Angenommen, die Variable
numberLength
wird nicht als endgültig deklariert, und Sie fügen die markierte Zuweisungsanweisung imPhoneNumber
Konstruktor hinzu:Aufgrund dieser Zuweisungsanweisung ist die Variable numberLength nicht mehr endgültig. Infolgedessen generiert der Java-Compiler eine Fehlermeldung ähnlich der Meldung "Lokale Variablen, auf die von einer inneren Klasse verwiesen wird, müssen endgültig oder effektiv endgültig sein", wobei die innere Klasse PhoneNumber versucht, auf die Variable numberLength zuzugreifen:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
quelle
numberLength
zu einer lokalen Variablen dieser Methode wird.Ich finde, der einfachste Weg, "effektiv endgültig" zu erklären, besteht darin, sich vorzustellen, den
final
Modifikator einer Variablendeklaration hinzuzufügen . Wenn sich das Programm mit dieser Änderung sowohl zur Kompilierungszeit als auch zur Laufzeit weiterhin auf dieselbe Weise verhält, ist diese Variable effektiv endgültig.quelle
case k
dass ein konstanter Ausdruck erforderlich ist, der eine konstante Variable sein kann ("Eine konstante Variable ist eine endgültige Variable vom primitiven Typ oder vom Typ String, die mit einem konstanten Ausdruck initialisiert wird" JLS 4.12.4 ). Dies ist ein Sonderfall eines final Variable.Laut den Dokumenten :
Wenn der Compiler feststellt, dass eine Variable nicht in Zuweisungen außerhalb ihrer Initialisierung angezeigt wird, wird die Variable als effektiv endgültig betrachtet .
Betrachten Sie zum Beispiel eine Klasse:
quelle
bar
In Ihrem Beispiel handelt es sich nicht um eine lokale Variable, sondern um ein Feld. "Effektiv endgültig" in der Fehlermeldung wie oben gilt überhaupt nicht für Felder.bar
ist hier ein Parameter, kein Feld.'Effectively final' ist eine Variable, die keinen Compilerfehler ausgibt, wenn sie an 'final' angehängt wird.
Aus einem Artikel von 'Brian Goetz',
Lambda-State-Finale-Brian Goetz
quelle
Diese Variable unten ist endgültig , daher können wir ihren Wert nach der Initialisierung nicht mehr ändern. Wenn wir es versuchen, erhalten wir einen Kompilierungsfehler ...
Aber wenn wir eine Variable wie diese erstellen, können wir ihren Wert ändern ...
In Java 8 sind jedoch standardmäßig alle Variablen endgültig . Das Vorhandensein der zweiten Zeile im Code macht sie jedoch nicht endgültig . Wenn wir also die 2. Zeile aus dem obigen Code entfernen, ist unsere Variable jetzt "effektiv endgültig" ...
Also .. Jede Variable, die einmal und nur einmal zugewiesen wird, ist "effektiv endgültig" .
quelle
Eine Variable ist endgültig oder effektiv endgültig, wenn sie einmal initialisiert wird und in ihrer Eigentümerklasse niemals mutiert ist . Und wir können es nicht in Schleifen oder inneren Klassen initialisieren .
Finale :
Effektiv endgültig :
quelle
Wenn ein Lambda-Ausdruck eine zugewiesene lokale Variable aus seinem umschließenden Raum verwendet, gibt es eine wichtige Einschränkung. Ein Lambda-Ausdruck darf nur lokale Variablen verwenden, deren Wert sich nicht ändert. Diese Einschränkung wird als " Variablenerfassung " bezeichnet, die beschrieben wird als; Erfassungswerte für Lambda-Ausdrücke, keine Variablen .
Die lokalen Variablen, die ein Lambda-Ausdruck verwenden kann, werden als " effektiv endgültig " bezeichnet.
Eine effektiv endgültige Variable ist eine Variable, deren Wert sich nach der ersten Zuweisung nicht ändert. Es ist nicht erforderlich, eine solche Variable explizit als final zu deklarieren, obwohl dies kein Fehler wäre.
Schauen wir uns ein Beispiel an: Wir haben eine lokale Variable i, die mit dem Wert 7 initialisiert wird. Im Lambda-Ausdruck versuchen wir, diesen Wert zu ändern, indem wir i einen neuen Wert zuweisen. Dies führt zu einem Compilerfehler - " Die in einem umschließenden Bereich definierte lokale Variable i muss endgültig oder effektiv endgültig sein. "
quelle
Das effektive letzte Thema ist in JLS 4.12.4 beschrieben und der letzte Absatz enthält eine klare Erklärung:
quelle
final ist eine Variable deklarieren mit Schlüsselwort
final
, Beispiel:es bleibt
final
während des gesamten Programms.effektiv endgültig : Jede lokale Variable oder jeder lokale Parameter, dem derzeit nur einmal ein Wert zugewiesen wird (oder der nur einmal aktualisiert wird). Es kann sein, dass es während des gesamten Programms nicht effektiv endgültig bleibt . Dies bedeutet also, dass die effektiv endgültige Variable ihre effektiv endgültige Eigenschaft verlieren kann, sobald sie mindestens eine weitere Zuweisung zugewiesen / aktualisiert hat. Beispiel:
quelle
final
Schlüsselwort nicht zu einer Deklaration hinzufügen können, ohne Kompilierungsfehler einzuführen, ist es nicht effektiv endgültig . Dies ist das Gegenteil dieser Aussage: "Wenn eine Variable tatsächlich endgültig ist, führt das Hinzufügen des endgültigen Modifikators zu ihrer Deklaration zu keinen Fehlern bei der Kompilierung."Wie andere gesagt haben, ist eine Variable oder ein Parameter, dessen Wert nach der Initialisierung niemals geändert wird, effektiv endgültig. Wenn Sie im obigen Code den Wert von
x
in der inneren Klasse ändern, gibtFirstLevel
der Compiler die folgende Fehlermeldung aus:quelle
Lambda-Ausdrücke können darauf zugreifen
statische Variablen,
Instanzvariablen,
effektiv endgültige Methodenparameter und
effektiv endgültige lokale Variablen.
Quelle: OCP: Oracle Certified Professional Java SE 8 Programmer II-Studienhandbuch, Jeanne Boyarsky, Scott Selikoff
Zusätzlich,
Quelle: Beginnend mit Java: Von Kontrollstrukturen über Objekte (6. Ausgabe), Tony Gaddis
Vergessen Sie außerdem nicht,
final
dass es genau einmal initialisiert wird, bevor es zum ersten Mal verwendet wird.quelle
Wenn Sie eine Variable
final
deklarieren oder nicht deklarieren, siefinal
jedoch endgültig halten, kann dies (abhängig vom Compiler) zu einem anderen Bytecode führen.Schauen wir uns ein kleines Beispiel an:
Der entsprechende Bytecode der
main
Methode (Java 8u161 unter Windows 64 Bit):Die entsprechende Zeilennummertabelle:
Da wir den Quellcode an den Linien zu sehen
12
,13
,14
erscheint nicht in dem Byte - Code. Dasi
liegttrue
daran, dass es seinen Zustand ändert und nicht ändern wird. Daher ist dieser Code nicht erreichbar (mehr in dieser Antwort ). Aus dem gleichen Grund9
fehlt auch der Code in der Zeile . Der Zustand voni
muss nicht ausgewertet werden, da diestrue
sicher ist.Auf der anderen Seite ist die Variable
j
zwar endgültig, wird aber nicht auf die gleiche Weise verarbeitet. Es werden keine derartigen Optimierungen angewendet. Der Zustand vonj
wird zweimal ausgewertet. Der Bytecode ist derselbe, unabhängig davonj
, ob er tatsächlich endgültig ist .quelle
Dies begann nicht mit Java 8, ich benutze dies seit langer Zeit. Dieser Code wurde (vor Java 8) verwendet, um legal zu sein:
quelle
final
Variablen zugegriffen werden kann, in Java 8 jedoch auch auf diejenigen, die effektiv endgültig sind.