Die in der Lambda-Expression verwendete Variable sollte endgültig oder effektiv final sein

Die in der Lambda-Expression verwendete Variable sollte endgültig oder effektiv final sein

Wenn ich versuche, calTz zu benutzen, calTz es diesen Fehler an.

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { try { cal.getComponents().getComponents("VTIMEZONE").forEach(component->{ VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(calTz==null) { calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); } }); } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return null; } 

Eine final Variable bedeutet, dass sie nur einmal instanziiert werden kann. In Java können Sie nicht-finale Variablen in Lambda sowie in anonymen inneren classn verwenden.

Sie können Ihren Code mit der alten for-each-Schleife umgestalten:

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { try { for(Component component : cal.getComponents().getComponents("VTIMEZONE")) { VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(calTz==null) { calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); } } } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return null; } 

Auch wenn ich einige Teile dieses Codes nicht verstehen kann:

  • Sie rufen eine v.getTimeZoneId(); ohne seinen Rückgabewert zu verwenden
  • mit der Zuweisung calTz = TimeZone.getTimeZone(v.getTimeZoneId().getValue()); Sie ändern nicht das ursprünglich übergebene calTz und Sie verwenden es nicht in dieser Methode
  • Sie geben immer null , warum setzen Sie nicht void als Rückgabetyp?

Hoffe auch diese Tipps helfen dir, dich zu verbessern.

Von einem Lambda können Sie keinen Hinweis auf etwas erhalten, das nicht endgültig ist. Sie müssen einen abschließenden Wrapper von außerhalb des Lamda deklarieren, um Ihre Variable zu halten.

Ich habe das letzte “Referenz” -Objekt als diesen Wrapper hinzugefügt.

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal,TimeZone calTz) { final AtomicReference reference = new AtomicReference<>(); try { cal.getComponents().getComponents("VTIMEZONE").forEach(component->{ VTimeZone v = (VTimeZone) component; v.getTimeZoneId(); if(reference.get()==null) { reference.set(TimeZone.getTimeZone(v.getTimeZoneId().getValue())); } }); } catch (Exception e) { //log.warn("Unable to determine ical timezone", e); } return reference.get(); } 

Java 8 hat ein neues Konzept namens “Effective final” Variable. Dies bedeutet, dass eine nicht finale lokale Variable, deren Wert sich nach der Initialisierung niemals ändert, “Effektiv Final” genannt wird.

Dieses Konzept wurde eingeführt, weil wir vor Java 8 keine nicht finale lokale Variable in einer anonymous class . Wenn Sie Zugriff auf eine lokale Variable in anonymous class haben möchten, müssen Sie sie endgültig machen.

Als lambda eingeführt wurde, wurde diese Einschränkung gelockert. Daher ist es notwendig, die lokale Variable final zu machen, wenn sie nicht geändert wird, sobald sie initialisiert ist, da Lambda selbst nichts anderes als eine anonyme class ist.

Java 8 erkannte, dass die lokale Variable final deklariert werden musste, wenn Entwickler lambda und führte dieses Konzept ein und machte es überflüssig, lokale Variablen final zu machen. Wenn Sie also sehen, dass die Regel für die anonymous class immer noch nicht geändert wurde, müssen Sie nicht jedes Mal, wenn Sie lambdas , das final Keyword schreiben.

Ich habe hier eine gute Erklärung gefunden

Obwohl andere Antworten die Anforderung belegen, erklären sie nicht, warum die Anforderung existiert.

Die JLS erwähnt warum in §15.27.2 :

Die Beschränkung auf effektiv endgültige Variablen verbietet den Zugriff auf sich dynamisch ändernde lokale Variablen, deren Erfassung wahrscheinlich Parallelitätsprobleme einführen würde.

Um das Risiko von Fehlern zu verringern, entschieden sie sich dafür zu sorgen, dass erfasste Variablen niemals mutiert werden.

In Ihrem Beispiel können Sie forEach mit lamdba durch eine einfache for Schleife ersetzen und beliebige Variablen frei modifizieren. Oder, wahrscheinlich, refaktorieren Sie Ihren Code, so dass Sie keine Variablen ändern müssen. Ich werde jedoch der Vollständigkeit halber erklären, was der Fehler bedeutet und wie man ihn umgehen kann.

Java 8 Sprachspezifikation, §15.27.2 :

Alle verwendeten lokalen Variablen, Formalparameter oder Ausnahmeparameter, die in einem Lambda-Ausdruck nicht deklariert sind, müssen entweder als final deklariert werden oder als endgültig gelten ( §4.12.4 ), oder es tritt ein Fehler bei der Kompilierung auf, wenn die Verwendung versucht wird.

Grundsätzlich können Sie eine lokale Variable (in diesem Fall calTz ) nicht innerhalb eines Lambda (oder einer lokalen / anonymen class) ändern. Um dies in Java zu erreichen, müssen Sie ein veränderbares Objekt verwenden und es (über eine letzte Variable) aus dem Lambda ändern. Ein Beispiel für ein veränderbares Objekt wäre hier ein Array aus einem Element:

 private TimeZone extractCalendarTimeZoneComponent(Calendar cal, TimeZone calTz) { TimeZone[] result = { null }; try { cal.getComponents().getComponents("VTIMEZONE").forEach(component -> { ... result[0] = ...; ... } } catch (Exception e) { log.warn("Unable to determine ical timezone", e); } return result[0]; } 

Wenn es nicht notwendig ist, die Variable zu modifizieren, wäre eine allgemeine Problemumgehung für diese Art von Problem, den Teil des Codes zu extrahieren, der lambda verwendet und final keyword für Methodenparameter verwendet.