it-swarm.com.de

Unterschied zwischen final und effektiv final

Ich spiele mit Lambdas in Java 8 und bin auf die Warnung local variables referenced from a lambda expression must be final or effectively final gestoßen. Ich weiß, dass, wenn ich Variablen innerhalb anonymer Klassen verwende, sie in der äußeren Klasse final sein müssen, aber - was ist der Unterschied zwischen final und effektiv final ?

292
alex

... ab Java SE 8 kann eine lokale Klasse auf lokale Variablen und Parameter des umgebenden Blocks zugreifen, die endgültig oder effektiv sind. Eine Variable oder ein Parameter, dessen Wert nach der Initialisierung nie geändert wird, ist tatsächlich endgültig.

Angenommen, die Variable numberLength ist nicht als final deklariert, und Sie fügen die markierte Zuweisungsanweisung im Konstruktor PhoneNumber hinzu:

public class OutterClass {  

  int numberLength; // <== not *final*

  class PhoneNumber {

    PhoneNumber(String phoneNumber) {
        numberLength = 7;   // <== assignment to numberLength
        String currentNumber = phoneNumber.replaceAll(
            regularExpression, "");
        if (currentNumber.length() == numberLength)
            formattedPhoneNumber = currentNumber;
        else
            formattedPhoneNumber = null;
     }

  ...

  }

...

}

Aufgrund dieser Zuweisungsanweisung ist die Variable numberLength nicht mehr effektiv final. Als Ergebnis generiert der Java-Compiler eine Fehlermeldung, die ähnlich ist wie "lokale Variablen, auf die von einer inneren Klasse verwiesen wird, oder endgültig endgültig ist", wobei die innere Klasse PhoneNumber versucht, auf die numberLength-Variable zuzugreifen:

http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html

http://docs.Oracle.com/javase/tutorial/Java/javaOO/localclasses.html

199
Suresh Atta

Der einfachste Weg, "effektiv final" zu erklären, besteht darin, sich vorzustellen, dass der Modifizierer final zu einer Variablendeklaration hinzugefügt wird. Wenn sich das Programm mit dieser Änderung weiterhin sowohl zur Kompilierzeit als auch zur Laufzeit auf die gleiche Weise verhält, ist diese Variable endgültig.

115

Nach den docs :

Eine Variable oder ein Parameter, dessen Wert nach der Initialisierung nie geändert wird, ist tatsächlich endgültig.

Wenn der Compiler feststellt, dass eine Variable außerhalb ihrer Initialisierung nicht in Zuweisungen erscheint, wird die Variable als effektiv final betrachtet.

Betrachten Sie beispielsweise eine Klasse:

public class Foo {

    public void baz(int bar) {
        // While the next line is commented, bar is effectively final
        // and while it is uncommented, the assignment means it is not
        // effectively final.

        // bar = 2;
    }
}
32
Mark Elliot

Aus einem Artikel von 'Brian Goetz'

'Effectively final' ist eine Variable, die dem Compiler keinen Fehler gibt wenn es von 'final' angehängt werden sollte

Lambda-State-Finale- Brian Goetz

25
Ajeet Ganga

Diese Variable ist final , daher können wir ihren Wert nach der Initialisierung nicht mehr ändern. Wenn wir versuchen, werden wir einen Kompilierungsfehler bekommen ...

final int variable = 123;

Wenn wir jedoch eine Variable wie diese erstellen, können wir ihren Wert ändern ...

int variable = 123;
variable = 456;

In Java 8 sind jedoch standardmäßig alle Variablen final . Aber die Existenz der zweiten Zeile im Code macht es nicht endgültig . Wenn wir also die 2. Zeile aus dem obigen Code entfernen, ist unsere Variable jetzt "effektiv final" ...

int variable = 123;

Also .. Jede Variable, die einmalig vergeben wird, ist "effektiv final" .

14
Eurig Jones

Wenn ein Lambda-Ausdruck eine zugewiesene lokale Variable aus dem umgebenden 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 " variable Erfassung " bezeichnet, die als beschrieben wird; Lambda-Ausdrucks-Capture-Werte, keine Variablen
Die lokalen Variablen, die ein Lambda-Ausdruck verwenden kann, werden als " effektiv final " bezeichnet. 
Eine effektiv endgültige Variable ist eine Variable, deren Wert sich nach der ersten Zuweisung nicht ändert. Es ist nicht notwendig, eine solche Variable explizit als final zu deklarieren, obwohl dies kein Fehler wäre.
Betrachten wir es mit einem Beispiel, wir haben eine lokale Variable i, die mit dem Wert 7 initialisiert wird, mit dem Lambda-Ausdruck versuchen wir, diesen Wert zu ändern, indem wir einen neuen Wert zuweisen. Dies führt zu einem Compiler-Fehler - "Lokale Variable, die in einem einschließenden Bereich definiert ist, muss final oder effektiv final sein"

@FunctionalInterface
interface IFuncInt {
    int func(int num1, int num2);
    public String toString();
}

public class LambdaVarDemo {

    public static void main(String[] args){             
        int i = 7;
        IFuncInt funcInt = (num1, num2) -> {
            i = num1 + num2;
            return i;
        };
    }   
}
6
infoj

Eine Variable ist final oder effektiv final, wenn einmal initialisiert und niemals mutiert in ihrer Besitzerklasse. Und wir können nicht initialisieren es in Schleifen oder innere Klassen.

Final:

final int number;
number = 23;

Effektiv endgültig:

int number;
number = 34;
6
samadadi

Effective final topic wird in JLS 4.12.4 beschrieben und der letzte Absatz enthält eine klare Erklärung:

Wenn eine Variable effektiv final ist, führt das Hinzufügen des final-Modifizierers zu ihrer Deklaration keine Fehler bei der Kompilierung aus. Umgekehrt wird eine lokale Variable oder ein Parameter, der in einem gültigen Programm als final deklariert wird, endgültig final, wenn der letzte Modifikator entfernt wird.

2
Dmitry N.
public class LambdaScopeTest {
    public int x = 0;        
    class FirstLevel {
        public int x = 1;    
        void methodInFirstLevel(int x) {

            // The following statement causes the compiler to generate
            // the error "local variables referenced from a lambda expression
            // must be final or effectively final" in statement A:
            //
            // x = 99; 

        }
    }    
}

Wie andere bereits gesagt haben, ist eine Variable oder ein Parameter, dessen Wert nach der Initialisierung nie geändert wird, tatsächlich endgültig. Wenn Sie im obigen Code den Wert von x in der inneren Klasse FirstLevel ändern, gibt der Compiler die Fehlermeldung aus:

Lokale Variablen, auf die von einem Lambda-Ausdruck verwiesen wird, müssen final oder effektiv final sein.

1
Girish

final ist eine Variablendeklaration mit Schlüsselwort final, Beispiel: 

final double pi = 3.14 ;

es bleibt final während des gesamten Programms. 

effektiv final : jede lokale Variable oder jeder Parameter, der gerade einmal einen Wert (oder nur einmal aktualisiert) hat. Es darf nicht effektiv final während des gesamten Programms bleiben. Dies bedeutet, dass effektiv final variable unmittelbar nach der Zuweisung/Aktualisierung mindestens einer weiteren Zuweisung ihre endgültige Eigenschaft verlieren kann. Beispiel:

class EffectivelyFinal {

    public static void main(String[] args) {
        calculate(124,53);
    }

    public static void calculate( int operand1, int operand2){   
     int rem = 0;  //   operand1, operand2 and rem are effectively final here
     rem = operand1%2  // rem lost its effectively final property here because it gets its second assignment 
                       // operand1, operand2 are still effectively final here 
        class operators{

            void setNum(){
                operand1 =   operand2%2;  // operand1 lost its effectively final property here because it gets its second assignment
            }

            int add(){
                return rem + operand2;  // does not compile because rem is not effectively final
            }
            int multiply(){
                return rem * operand1;  // does not compile because both rem and operand1 are not effectively final
            }
        }   
   }    
}

Wenn Sie den Modifizierer final zu einer lokalen Variablen hinzufügen könnten, war es effektiv endgültig.

Lambda-Ausdrücke können zugreifen 

  • statische Variablen, 

  • instanzvariablen,

  • effektiv endgültige Methodenparameter und 

  • effektiv final lokale Variablen.

Quelle: OCP: Oracle Certified Professional Java SE 8-Programmierer II-Studienhandbuch, Jeanne Boyarsky, Scott Selikoff

Zusätzlich, 

Eine effectively final-Variable ist eine Variable, deren Wert niemals .__ ist. geändert, aber nicht mit dem Schlüsselwort final deklariert.

Quelle: Start mit Java: Von Kontrollstrukturen über Objekte (6. Ausgabe), Tony Gaddis

Vergessen Sie auch nicht die Bedeutung von final, dass sie genau einmal initialisiert wird, bevor sie zum ersten Mal verwendet wird.

1
snr

Das Deklarieren einer Variablen final oder das Deklarieren der Variable final, aber das Beibehalten final final kann zu unterschiedlichen Bytecodes (abhängig vom Compiler) führen.

Schauen wir uns ein kleines Beispiel an:

    public static void main(String[] args) {
        final boolean i = true;   // 6  // final by declaration
        boolean j = true;         // 7  // effectively final

        if (i) {                  // 9
            System.out.println(i);// 10
        }
        if (!i) {                 // 12
            System.out.println(i);// 13
        }
        if (j) {                  // 15
            System.out.println(j);// 16
        }
        if (!j) {                 // 18
            System.out.println(j);// 19
        }
    }

Der entsprechende Bytecode der main-Methode (Java 8u161 unter Windows 64 Bit):

  public static void main(Java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_1
       3: istore_2
       4: getstatic     #16                 // Field Java/lang/System.out:Ljava/io/PrintStream;
       7: iconst_1
       8: invokevirtual #22                 // Method Java/io/PrintStream.println:(Z)V
      11: iload_2
      12: ifeq          22
      15: getstatic     #16                 // Field Java/lang/System.out:Ljava/io/PrintStream;
      18: iload_2
      19: invokevirtual #22                 // Method Java/io/PrintStream.println:(Z)V
      22: iload_2
      23: ifne          33
      26: getstatic     #16                 // Field Java/lang/System.out:Ljava/io/PrintStream;
      29: iload_2
      30: invokevirtual #22                 // Method Java/io/PrintStream.println:(Z)V
      33: return

Die entsprechende Zeilennummerentabelle:

 LineNumberTable:
   line 6: 0
   line 7: 2
   line 10: 4
   line 15: 11
   line 16: 15
   line 18: 22
   line 19: 26
   line 21: 33

Da wir den Quellcode in den Zeilen 12, 13 sehen, erscheint 14 nicht im Bytecode. Das liegt daran, dass itrue ist und seinen Status nicht ändern wird. Somit ist dieser Code nicht erreichbar (mehr in dieser Antwort ). Aus demselben Grund fehlt auch der Code in Zeile 9. Der Zustand von i muss nicht ausgewertet werden, da er true ist.

Auf der anderen Seite, obwohl die Variable jeffektiv final ist, wird sie nicht auf dieselbe Weise verarbeitet. Es werden keine derartigen Optimierungen angewendet. Der Zustand von j wird zweimal ausgewertet. Der Bytecode ist der gleiche, unabhängig davon, ob jeffektiv final ist.

0
LuCio