Java >> Java tutorial >  >> Tag >> final

Final vs Effektiv 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 finalen modifikator for variabler, felter og parametre, der effektivt behandles og bruges som endelige.

I dette selvstudium vil vi udforske denne funktions oprindelse, og hvordan den behandles af compileren sammenlignet med den endelige søgeord . Desuden vil vi udforske en løsning at bruge i forbindelse med en problematisk use-case af effektivt endelige variabler.

2. Effektivt Final Origin

Enkelt sagt er objekter eller primitive værdier faktisk 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 den faktisk endelig - også selvom der sker en ændring i tilstanden af ​​det refererede objekt.

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

Anonyme klasser er indre klasser, og de kan ikke få adgang til ikke-endelige eller ikke-effektivt endelige variabler eller mutere dem i deres omsluttende omfang som specificeret af JLS 8.1.3. Den samme begrænsning gælder for lambda-udtryk, da adgang potentielt kan give samtidighedsproblemer.

3. Endelig vs. endelig endelig

Den nemmeste måde at forstå, om en endelig variabel er faktisk endelig, er at overveje, om du fjerner finalen søgeord ville tillade koden at kompilere og køre:

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

Gentildeling af en værdi eller mutation af ovenstående endelige variabel ville gøre koden ugyldig, uanset hvor den forekommer.

3.1. Kompilerbehandling

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

Java-kompileren udfører ikke yderligere optimering for effektivt endelige variabler, i modsætning til det gør for final variabler.

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

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

Compileren vil ændre koden, der udføres i main metoden ovenfor til:

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

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

4. Atomændring

Genereltdet er ikke en god praksis at ændre variabler, der bruges i lambda-udtryk og anonyme klasser . Vi kan ikke vide, hvordan disse variabler vil blive brugt inde i metodeblokke. Mutation af dem kan føre til uventede resultater i multithreading-miljøer.

Vi har allerede et selvstudie, der forklarer de bedste fremgangsmåder ved brug af lambda-udtryk, og et andet, der forklarer almindelige anti-mø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, som opnår trådsikkerhed gennem atomicitet.

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

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

5. Konklusion

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


Java tag