2010-05-10 9 views
20

In Java eine anonyme innere Klasse auf Variablen in nachschlagen zu können ist lokaler Bereich:Wie implementiert Java innere Klassenschließungen?

public class A { 
    public void method() { 
     final int i = 0; 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 
    } 
} 

ist meine Frage, wie ist dies tatsächlich umgesetzt? Wie kommt i zu der anonymen inneren doAction Implementierung, und warum muss es final sein?

Antwort

11

Der Compiler generiert automatisch einen Konstruktor für Ihre anonyme innere Klasse und übergibt Ihre lokale Variable an diesen Konstruktor.

Der Konstruktor speichert diesen Wert in einer Klassenvariablen (einem Feld), auch i genannt, die innerhalb der "Schließung" verwendet wird.

Warum muss es endgültig sein? Nun lassen Sie uns die Situation erkunden in denen es nicht:

public class A { 
    public void method() { 
     int i = 0; // note: this is WRONG code 

     doStuff(new Action() { 
      public void doAction() { 
       Console.printf(i); // or whatever 
      } 
     }); 

     i = 4; // A 
     // B 
     i = 5; // C 
    } 
} 

In Situation A das Feld i von Action auch geändert werden muss, nehmen wir an, dies ist möglich: es muss die Referenz auf das Objekt Action.

Angenommen, in Situation B ist diese Instanz von Action Müll gesammelt.

Jetzt in Situation C: es benötigt eine Instanz von Action, um seine Klassenvariable zu aktualisieren, aber der Wert ist GCed. Es muss "wissen", dass es GCed ist, aber das ist schwierig.

Um die Implementierung der VM zu vereinfachen, haben die Java-Sprachentwickler gesagt, dass sie endgültig sein sollten, so dass die VM keine Möglichkeit zum Überprüfen, ob ein Objekt verschwunden ist, und garantiert, dass die Variable nicht ist modifiziert, und dass die VM oder der Compiler nicht alle Verwendungen der Variablen in anonymen inneren Klassen und deren Instanzen referenzieren muss.

+0

Eigentlich die synthetisierte Variable, die eine Kopie der Variablen hält i nicht genannt. Abhängig davon, welche Version des Compilers Sie verwenden, ist es "$ i" oder "+ i". –

15

Lokale Variablen sind (offensichtlich) nicht zwischen verschiedenen Methoden wie method() und doAction() oben geteilt. Aber da es endgültig ist, kann in diesem Fall nichts "Schlechtes" passieren, so dass die Sprache es immer noch erlaubt. Der Compiler muss jedoch etwas kluges über die Situation tun. Werfen wir einen Blick darauf, was javac produziert:

$ javap -v "A\$1"   # A$1 is the anonymous Action-class. 
... 
final int val$i; // A field to store the i-value in. 

final A this$0;  // A reference to the "enclosing" A-object. 

A$1(A, int); // created constructor of the anonymous class 
    Code: 
    Stack=2, Locals=3, Args_size=3 
    0: aload_0 
    1: aload_1 
    2: putfield #1; //Field this$0:LA; 
    5: aload_0 
    6: iload_2 
    7: putfield #2; //Field val$i:I 
    10: aload_0 
    11: invokespecial #3; //Method java/lang/Object."<init>":()V 
    14: return 
    ... 
public void doAction(); 
    Code: 
    Stack=2, Locals=1, Args_size=1 
    0: getstatiC#4; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: aload_0 
    4: getfield #2; //Field val$i:I 
    7: invokevirtual #5; //Method java/io/PrintStream.println:(I)V 
    10: return 

Dies zeigt eigentlich, dass es

  • die i Variable in ein Feld gedreht,
  • einen Konstruktor für die anonyme Klasse erstellt, die einen Verweis akzeptiert an das A Objekt
  • , auf das später in der doAction() Methode zugegriffen wurde.

(Nebenbemerkung:. I die Variable new java.util.Random().nextInt() zu initialisieren hatte es zu verhindern, dass die Optimierung eine Menge Code entfernt)


ähnliche Diskussionen hier

method local innerclasses accessing the local variables of the method

+2

Es hat nichts (viel) mit einem Gewinde zu tun. Es ist nur ein Nebeneffekt. Nizza java disasm, btw: gibt einen guten Einblick in den Compiler. – Pindatjuh

+0

Sie haben Recht. Ich werde es überarbeiten. Danke für den Zeiger. – aioobe

+0

@Pindatjuh, aktualisiert die DISASM ... klar, dass es eine Menge Code als Compiler realisiert wegoptimiert 'I' war immer 0. – aioobe

3

Die lokale Klasseninstanz (die anonyme Klasse) muss eine separate Kopie der Variablen verwalten, da sie die Funktion möglicherweise überlebt. Um nicht die Verwechslung von zwei veränderbaren Variablen mit demselben Namen im selben Gültigkeitsbereich zu haben, wird die Variable gezwungen, endgültig zu sein.

Weitere Details finden Sie unter Java Final - an enduring mystery.

Verwandte Themen