# Innere Klasse und final



## WeirdAl (10. Mrz 2008)

Hallo,
ich habe eine Verständnisfrage zum Thema Innere Klassen.

Wenn ich von einer Inneren Klasse aus auf eine lokale Variable der Äußeren Klasse zugreifen möchte, geht dies ja nur wenn diese als final deklariert ist. 

Meine Frage lautet jetzt: Warum ist das so?

Cu
Alex


----------



## quippy (10. Mrz 2008)

Nach meinem Verständnis ist das folgendermaßen (kein Anspruch, daß das korrekt ist):

final deklarierte Variablen MÜSSEN initialisiert werden - ihr Wert kann sich danach nicht mehr ändern. Da inner-Classes möglicherweise nicht wissen, wer ihre Vater-Instanz ist, können Sie nicht direkt auf den Inhalt der Variablen zugreifen, er muss daher wahrscheinlich fest initialisiert sein (Quasi konstant), um dann in die Inner Class beim Instantiieren derselben hineinkopiert zu werden.

Näheres könnte man erfahren, wenn man sich den Bytecode ansieht.

Um das Problem zu umgehen, würde ich der InnerClass einen Konstruktor oder eine Setter spendieren, der man die Vater-Instanz mitgibt. Dann könntest Du den benötigten Wert per Getter der Inner-Class zur Verfügung stellen.


----------



## quippy (10. Mrz 2008)

Noch ein paar Links:

http://java.sun.com/docs/books/jls/second_edition/html/defAssign.doc.html#25979

http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html ab dem Punkt "8.1.2 Inner Classes and Enclosing Instances"

http://www.javaworld.com/javaworld/jw-10-1997/jw-10-indepth.html könnte auch noch hilfreich sein.


----------



## SlaterB (10. Mrz 2008)

> er muss daher wahrscheinlich fest initialisiert sein (Quasi konstant), 

stimmt nicht, es kann auch ein finaler Parameter der Operation
oder final double d = Math.random(); oder sonstwas sein,

> final deklarierte Variablen MÜSSEN initialisiert werden 

lokale Variablen müssen sowieso alle vor Gebrauch initialisiert werden, auch in normalen Code,
mit Exemplarvariablen/ Klassenattributen gehts übrigens auch nicht-final, das Vater-Objekt ist also bekannt

-----------

für mich gibt es keinen logischen Grund, warum das so gebaut ist,
außer, den Programmier etwas zu lenken, so dass eindeutig ist, welcher Wert in die innere Klasse kommt


```
public class Test
{
    public static void main(String[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            final int finalI = i;
            Object o = new Object()
                {
                    public String toString()
                    {
                        return "i: " + finalI;
                    }
                };
            System.out.println(o);
        }
    }
}
```
funktioniert, warum direkt mit i nicht? 
diesen Standard 'Variablen nehmen, als final deklarieren, in innerer Klasse auf finale Variable zugreifen' könnte der Compiler genausogut, da ist nix besonderes dabei,

allein um dies nicht zu verstecken, sondern den User sichtbar zu machen,
wird es dem User aufgebunden, nehme ich an


----------



## quippy (10. Mrz 2008)

Das ist genau das, was ich meine, denn für deine anonyme Klasse ist "finalI" dann konstant (deswegen "quasi konstant")


----------



## SlaterB (10. Mrz 2008)

ja und? jede Variable ist zwischen zwei Änderungen quasi-konstant..
welchen Sinn hat diese Aussage?
wenn ich eine Unteroperation aufrufe, dann ist für diese Zeit der Parameter ja quasi auch konstant..


----------
aber immerhin kommt noch dazu, dass man nicht anfangen sollte, in der inneren Klasse die Variable zu ändern,

das wäre aber einfacher zu prüfen, indem man solche Versuche in der inneren Klasse anstreicht
(Variable i is constant in inner class, do not modify)


----------



## quippy (11. Mrz 2008)

Nun ich hatte halt den Tipp abgegeben, daß inner Classes auf Variablen außerhalb nicht per Referenz zugreifen können - vielleicht, weil die Adresse außerhalb ihres Scopes liegt.

Daher wird vielleicht der Inhalt wohl bei Zugriff auf die Klasse dort hin kopiert. Daher darf sich der Inhalt der Variablen außerhalb nicht ändern, was er natürlich jederzeit könnte. Durch die "final" deklaration wird das explizit verhindert, so daß man davon ausgehen kann, daß sich "finalI" nie nie niemlas zwischen dessen Deklaration/einmaliger Definition und dem Aufruf in die anonyme Inner Class ändert wird.

Deine Alternative mit der Warnung würde zwar auf eine mögliche Gefahrenquelle hindeuten, aber nicht explizit verhindern, was zu Problemen führen könnte.

Mit der gleichen Logik könnte ich auch die Sichtbarkeitsregeln abschaffen, frei nach dem Motto: auf Membervariablen mit spendierter Zugriffs-Methode greift man nie direkt zu - selbst, wenn man es kann. Das bedeutet eben, daß sich Entwickler verantwortungsvoll mit dem Code auseinandersetzen. Und das tun i.d.R. nur noch wenige!


----------



## SlaterB (11. Mrz 2008)

gut, so gesehen macht es Sinn,
das entspricht ja meine Erklärung 'den Programmier etwas zu lenken, so dass eindeutig ist, welcher Wert in die innere Klasse kommt'

hat nix mit Initalisierung oder anderen technischen Gründen zu tun,
ein purer Nerv-Faktor für den User, der dafür falsche Vorstellungen erspart


----------



## Der Müde Joe (11. Mrz 2008)

für eine gute Erklärung

http://mindprod.com/jgloss/nestedclasses.html


----------



## quippy (11. Mrz 2008)

Hey, vielen Dank, Joe!

Hier die Essenz zum aktuellen Problem daraus, für die, die nicht alles lesen wollen:


> The rule is anonymous inner classes may only access final local variables of the enclosing method. Why? Because the inner class’s methods may be invoked later, long after the method that spawned it has terminated, e.g. by an AWT event. The local variables are long gone. The anonymous class then must work with flash frozen copies of just the ones it needs squirreled away covertly by the compiler in the anonymous inner class object.
> 
> You might ask, why do the local variables have to be final? Could not the compiler just as well take a copy of non-final local variables, much the way it does for a non-final parameters? If it did so, you would have two copies of the variable. Each could change independently, much like caller and callee’s copy of a parameter, however you would use the same syntax to access either copy. This would be confusing. So Sun insisted the local be final. This makes irrelevant that there are actually two copies of it.



Und ja, da fällt einem auf, daß gerade z.B. bei einem ActionListener zum Zeitpunkt dessen Aufrufs die ggf. referenzierte Variable ja gar nicht mehr da ist.
Also wird sie tatsächlich in die Klasse hineinkopiert - und da dann zwei Varianten existieren, die unabhängig geändert werden könnten, sollen sie "final" sein - damit genau das nicht passiert.


----------



## Der Müde Joe (11. Mrz 2008)

Ums noch etwas zu konkretisieren:

Der Heap dient zur Spreicherung von dynamischen Informationen über konkrete Objekte.
Dazu gehören auch Innere.

Nun Methoden speicher ihre lokalen Variablen aber im Stack ab. Kommt man in eine
Methode rein, wird ein Frame in den Stack gepushed, verlässt man die Methode (retun, Exception)
wird das FRame wieder von Stack genommen.

Nun das final bewirkt, dass die lokale Variable nicht in den Stack des entsrechenden Threads
geschrieben wird, sonder tiefer, also auf den Heap gelegt wird.

Somit bleibt diese Variable für die innere Klasse verlässlich erhalten.

(hoffe blabere keinen Müll) :wink:


----------



## SlaterB (11. Mrz 2008)

> Somit bleibt diese Variable für die innere Klasse verlässlich erhalten. 

werden alle final Variablen für alle Zeit irgendwo abgespeichert?
dann ist der Speicher ja irgendwann voll 

dass die Variable gespeichert wird hat nix mit final, Heap, Stack oder sonstigen lustigen Wörtern zu tun,
das macht Java (irgendwie) und fertig ist die Geschichte

final wie gesagt allein um den User zu lenken, dass er nicht denkt, dass die Variable noch geändert werden kann


----------

