388 Stimmen

Unterschied zwischen final und effektiv final

Ich spiele mit Lambdas in Java 8 und bin auf die Warnung lokale Variablen, auf die von einem Lambda-Ausdruck verwiesen wird, müssen endgültig oder effektiv endgültig sein gestoßen. Ich weiß, dass wenn ich Variablen innerhalb einer anonymen Klasse verwende, sie in der äußeren Klasse final sein müssen, aber trotzdem - was ist der Unterschied zwischen final und effektiv endgültig?

1voto

snr Punkte 15782

Wenn Sie das final-Schlüsselwort einer lokalen Variablen hinzufügen könnten, war es effektiv final.

Lambda-Ausdrücke können zugreifen

  • auf statische Variablen,

  • Instanzvariablen,

  • effektiv finale Methodenparameter und

  • effektiv finale lokale Variablen.

Quelle: OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide, Jeanne Boyarsky, Scott Selikoff

Zusätzlich,

Eine effektiv finale Variable ist eine Variable, deren Wert nie verändert wird, die jedoch nicht mit dem final-Schlüsselwort deklariert ist.

Quelle: Starting Out with Java: From Control Structures through Objects (6th Edition), Tony Gaddis

Vergessen Sie außerdem nicht die Bedeutung von final, dass es genau einmal initialisiert wird, bevor es zum ersten Mal verwendet wird.

0voto

LuCio Punkte 4665

Das Deklarieren einer Variablen final oder das Nicht-Deklarieren als final, aber sie als effektiv final zu halten, kann (je nach Compiler) zu unterschiedlichem Bytecode führen.

Werfen wir einen Blick auf ein kleines Beispiel:

    public static void main(String[] args) {
        final boolean i = true;   // 6  // final durch Deklaration
        boolean j = true;         // 7  // effektiv 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 Methode main (Java 8u161 auf 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 Zeilennummertabelle:

 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

Wie wir sehen, erscheint der Quellcode in den Zeilen 12, 13, 14 nicht im Bytecode. Das liegt daran, dass i true ist und seinen Zustand nicht ändern wird. Daher ist dieser Code unerreichbar (mehr dazu in dieser Antwort). Aus dem gleichen Grund fehlt auch der Code in Zeile 9. Der Zustand von i muss nicht ausgewertet werden, da er sicherlich true ist.

Andererseits, obwohl die Variable j effektiv final ist, wird sie nicht auf die gleiche Weise verarbeitet. Es werden keine solchen Optimierungen angewendet. Der Zustand von j wird zweimal evaluiert. Der Bytecode ist unabhängig davon, ob j effektiv final ist, der gleiche.

0voto

Jimmy_Rw Punkte 991

Die Effectively-Final-Variable ist eine lokale Variable, die:

  1. Nicht als final definiert ist.
  2. Nur einmal zugewiesen wird.

Im Gegensatz dazu ist eine final-Variable eine Variable, die:

  1. mit einem final Schlüsselwort deklariert ist.

0voto

Dush Punkte 11

Es gibt drei Arten von finalen Variablen, die in Java deklariert werden.

1. Implizit deklarierte finale Variablen

Es gibt 3 Arten von implizit deklarierten finalen Variablen.

  • Felder, die in Schnittstellen deklariert sind.
  • Lokale Variablen, die als Ressource in einer try-with-resource-Anweisung deklariert sind.
  • Ausnahmeparameter in einem Multi-Try-Block.

2. Explizit deklarierte finale Variablen

Dies sind Variablen, die mit dem final-Schlüsselwort deklariert sind.

3. Effektiv finale Variablen

Variablen gelten als effektiv final, wenn:

i. Eine lokale Variable in einer Anweisung deklariert wird und deren Deklarator einen Initialisierer hat, ODER eine lokale Variable nach einem Muster deklariert wird und den folgenden Regeln folgen sollte:

  1. nicht mit final deklariert
  2. nie als Wertzuweisung vorkommen
  3. nie als Operand eines Präfix- oder Postfix-Inkrement- oder Dekrementoperators vorkommen

z.B. Eine lokale Variable in einer Anweisung deklariert wird und deren Deklarator einen Initialisierer hat.

List ls = Arrays.asList("A", "B", "C");

for (Iterator iterator = ls.iterator(); iterator.hasNext();) {
  String s = (String) iterator.next();
  System.out.println(s);
}

Hier ist die Variable iterator effektiv final, da sie den oben genannten Regeln folgt.

Hinweis: Wenn eine Variable effektiv final ist, kann sie auch mit dem final-Schlüsselwort deklariert werden.

List ls = Arrays.asList("A", "B", "C");

for (final Iterator iterator = ls.iterator(); iterator.hasNext();) {
  String s = (String) iterator.next();
  System.out.println(s);
}

z.B. Lokale Variable nach einem Muster deklariert wird.

public String getStringfromObj(Object o){

    if (o instanceof String s && s.startsWith("Java")) { // kann als final String s deklariert werden
      return s;
    }

    return "";
}

ii. Eine lokale Variable wird in einer Anweisung deklariert und später initialisiert. Dies sollte den folgenden Regeln folgen.

  1. nicht mit final deklariert
  2. nie als Wertzuweisung vor und nach einer bestimmten Zuweisung vorkommen
  3. nie als Operand eines Präfix- oder Postfix-Inkrement- oder Dekrementoperators vorkommen

z.B.

List ls = Arrays.asList("A", "B", "C");
int k; // kann als final int k deklariert werden

    if ((k = ls.size()) > 0) {
      System.out.println(k);
}

iii. Ein Methode, Konstruktor, Lambda oder Ausnahmeparameter, dessen Deklarator einen Initialisierer hat.

-6voto

FiruzzZ Punkte 535

Allerdings kann eine lokale Klasse ab Java SE 8 auf lokale Variablen und Parameter des umgebenden Blocks zugreifen, die final oder effektiv final sind.

Dies begann nicht in Java 8, ich benutze das schon lange. Dieser Code war (vor Java 8) legal:

String str = ""; //<-- nicht zugänglich von anonymen Klassenimplementierungen
final String strFin = ""; //<-- zugänglich
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
         String ann = str; // <---- Fehler, muss final sein (IDE gibt den Hinweis);
         String ann = strFin; // <---- legal;
         String str = "legale Anweisung in Java 7,"
                +"Java 8 erlaubt dies nicht, es denkt, dass ich versuche, den vor der anonymen Implementierung deklarierten str zu verwenden.";
         // wir sind gezwungen, einen anderen Namen als str zu verwenden
    }
);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X