Final vs Effektiv Final in Java

1. Einleitung

Eine der interessantesten Funktionen, die in Java 8 eingeführt wurden, ist praktisch endgültig. Es erlaubt uns, den endgültigen Modifikator nicht für Variablen, Felder und Parameter zu schreiben , die effektiv wie endgültige behandelt und verwendet werden.

In diesem Tutorial untersuchen wir den Ursprung dieser Funktion und wie sie vom Compiler im Vergleich zum endgültigen Schlüsselwort behandelt wird . Darüber hinaus werden wir eine Lösung für einen problematischen Anwendungsfall effektiv endgültiger Variablen untersuchen.

2. Effektiv endgültiger Ursprung

In einfachen Worten, Objekte oder primitive Werte sind effektiv endgültig, wenn wir ihre Werte nach der Initialisierung nicht ändern . Wenn wir bei Objekten die Referenz eines Objekts nicht ändern, ist dies praktisch endgültig - selbst wenn sich der Status des referenzierten Objekts ändert.

Vor seiner Einführung konnten wir keine nicht endgültige lokale Variable in einer anonymen Klasse verwenden . Wir können immer noch keine Variablen verwenden, denen in anonymen Klassen, inneren Klassen und Lambda-Ausdrücken mehr als ein Wert zugewiesen ist. Durch die Einführung dieser Funktion müssen wir den endgültigen Modifikator nicht für Variablen verwenden, die effektiv endgültig sind, wodurch wir einige Tastenanschläge sparen.

Anonyme Klassen sind innere Klassen und können nicht auf nicht endgültige oder nicht effektiv endgültige Variablen zugreifen oder sie in ihren umschließenden Bereichen mutieren, wie in JLS 8.1.3 angegeben. Die gleiche Einschränkung gilt für Lambda-Ausdrücke, da der Zugriff möglicherweise zu Parallelitätsproblemen führen kann.

3. Finale gegen effektiv Finale

Der einfachste Weg zu verstehen, ob eine endgültige Variable tatsächlich endgültig ist, besteht darin, zu überlegen, ob das Entfernen des endgültigen Schlüsselworts das Kompilieren und Ausführen des Codes ermöglichen würde:

@FunctionalInterface public interface FunctionalInterface { void testEffectivelyFinal(); default void test() { int effectivelyFinalInt = 10; FunctionalInterface functionalInterface = () -> System.out.println("Value of effectively variable is : " + effectivelyFinalInt); } } 

Das Neuzuweisen eines Werts oder das Mutieren der oben genannten effektiven endgültigen Variablen würde den Code ungültig machen, unabhängig davon, wo er auftritt.

3.1. Compiler-Behandlung

JLS 4.12.4 besagt, dass wenn wir den endgültigen Modifikator aus einem Methodenparameter oder einer lokalen Variablen entfernen , ohne Fehler bei der Kompilierung einzuführen, dieser effektiv endgültig ist. Wenn wir außerdem das letzte Schlüsselwort zur Deklaration einer Variablen in einem gültigen Programm hinzufügen , ist es effektiv endgültig.

Der Java-Compiler führt keine zusätzliche Optimierung für effektiv endgültige Variablen durch, im Gegensatz zu endgültigen Variablen.

Betrachten wir ein einfaches Beispiel, das zwei endgültige String- Variablen deklariert , diese jedoch nur zur Verkettung verwendet:

public static void main(String[] args) { final String hello = "hello"; final String world = "world"; String test = hello + " " + world; System.out.println(test); } 

Der Compiler würde den in den ausgeführten Code ändern Hauptverfahren , das oben an:

public static void main(String[] var0) { String var1 = "hello world"; System.out.println(var1); }

Wenn wir andererseits die endgültigen Modifikatoren entfernen , werden die Variablen als effektiv endgültig betrachtet, aber der Compiler wird sie nicht entfernen, da sie nur zur Verkettung verwendet werden.

4. Atomare Modifikation

Im Allgemeinen ist es nicht empfehlenswert, Variablen zu ändern, die in Lambda-Ausdrücken und anonymen Klassen verwendet werden . Wir können nicht wissen, wie diese Variablen in Methodenblöcken verwendet werden. Das Mutieren kann in Multithreading-Umgebungen zu unerwarteten Ergebnissen führen.

Wir haben bereits ein Tutorial, in dem die Best Practices bei der Verwendung von Lambda-Ausdrücken erläutert werden, und ein weiteres, in dem gängige Anti-Patterns erläutert werden, wenn wir sie ändern. Es gibt jedoch einen alternativen Ansatz, mit dem wir Variablen in solchen Fällen ändern können, um die Thread-Sicherheit durch Atomizität zu erreichen.

Das Paket java.util.concurrent.atomic bietet Klassen wie AtomicReference und AtomicInteger . Wir können sie verwenden, um Variablen in Lambda-Ausdrücken atomar zu modifizieren:

public static void main(String[] args) { AtomicInteger effectivelyFinalInt = new AtomicInteger(10); FunctionalInterface functionalInterface = effectivelyFinalInt::incrementAndGet; }

5. Schlussfolgerung

In diesem Tutorial haben wir die bemerkenswertesten Unterschiede zwischen endgültigen und effektiv endgültigen Variablen kennengelernt. Darüber hinaus haben wir eine sichere Alternative bereitgestellt, mit der wir Variablen innerhalb von Lambda-Funktionen ändern können.