Final vs Effektivt Final i Java

1. Introduktion

En af de mest interessante funktioner introduceret i Java 8 er faktisk endelig. Det giver os mulighed for ikke at skrive endelig modifikator for variabler, felter og parametre, der behandles effektivt og bruges som endelige.

I denne vejledning udforsker vi denne funktion oprindelse og hvordan den behandles af compileren i forhold til endelig nøgleord. Derudover vil vi undersøge en løsning, der skal bruges vedrørende et problematisk brugstilfælde af effektivt endelige variabler.

2. Effektiv endelig oprindelse

Enkelt sagt objekter eller primitive værdier er effektivt endelige, hvis vi ikke ændrer deres værdier efter initialisering. I tilfælde af objekter, hvis vi ikke ændrer referencen for et objekt, så er det faktisk endelig - selvom der sker en ændring i tilstanden for det refererede objekt.

Før introduktionen vi kunne ikke bruge en ikke-endelig lokal variabel i en anonym klasse. Vi kan stadig ikke bruge variabler, der har tildelt mere end en værdi inden for anonyme klasser, indre klasser og lambda-udtryk. Indførelsen af ​​denne funktion giver os mulighed for ikke at skulle bruge endelig modifikator på variabler, der faktisk er endelige, hvilket sparer os et par tastetryk.

Anonyme klasser er indre klasser, og de kan ikke få adgang til ikke-endelige eller ikke-effektive-endelige variabler eller mutere dem i deres omslutningsomfang som specificeret i JLS 8.1.3. Den samme begrænsning gælder for lambda-udtryk, da adgang kan potentielt give problemer med samtidighed.

3. Final vs Effektivt Final

Den enkleste måde at forstå, om en endelig variabel faktisk er endelig, er at tænke, om fjernelse af endelig nøgleord tillader koden at kompilere og køre:

@FunctionalInterface offentlig grænseflade FunctionalInterface {ugyldig testEffectivelyFinal (); standard ugyldig test () {int effektivFinalInt = 10; FunctionalInterface functionalInterface = () -> System.out.println ("Værdien af ​​effektiv variabel er:" + effektivtFinalInt); }} 

Omfordeling af en værdi eller mutering af ovenstående effektivt endelige variabel ville gøre koden ugyldig uanset hvor den forekommer.

3.1. Compiler behandling

JLS 4.12.4 siger, at hvis vi fjerner endelig modifikator fra en metodeparameter eller en lokal variabel uden at indføre kompileringstidsfejl, så er den effektivt endelig. Desuden, hvis vi tilføjer endelig nøgleord til en variabels erklæring i et gyldigt program, så er det effektivt endeligt.

Java-kompilatoren foretager ikke yderligere optimering for effektivt endelige variabler, i modsætning til det gør for endelig variabler.

Lad os overveje et simpelt eksempel, der erklærer to sidste streng variabler, men bruger dem kun til sammenkædning:

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

Compileren ændrer den kode, der udføres i vigtigste ovenstående metode til:

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

På den anden side, hvis vi fjerner endelig modifikatorer, ville variablerne blive betragtet som effektive endelige, men compileren fjerner dem ikke da de kun bruges til sammenkædning.

4. Atomisk modifikation

Generelt, det er ikke en god praksis at ændre variabler, der bruges i lambda-udtryk og anonyme klasser. Vi kan ikke vide, hvordan disse variabler skal bruges i metodeblokke. At mutere dem kan føre til uventede resultater i multithreading-miljøer.

Vi har allerede en tutorial, der forklarer de bedste fremgangsmåder, når vi bruger lambda-udtryk, og en anden, der forklarer almindelige antimønstre, når vi ændrer dem. Men der er en alternativ tilgang, der giver os mulighed for at ændre variabler i sådanne tilfælde, der opnår trådsikkerhed gennem atomicitet.

Pakken java.util.concurrent.atomic tilbyder klasser som AtomicReference og AtomicInteger. Vi kan bruge dem til atomisk ændre variabler inde i lambda-udtryk:

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

5. Konklusion

I denne vejledning lærte vi om de mest bemærkelsesværdige forskelle mellem endelig og effektivt endelige variabler. Derudover leverede vi et sikkert alternativ, der giver os mulighed for at ændre variabler inde i lambda-funktioner.