dann bedeutet das doch, dass ich den Variablenwert nicht ändern kann. Da es sich hier um eine Referenzvariable handelt, dürfte also der Referenzwert nicht verändert werden können. Dieser ist doch defaultmässig null. Das würde doch bedeuten das ich die null Referenz nicht mehr ändern kann, oder?
Warum funktioniert es aber wenn ich einen Konstruktor schreibe ala
Java:
publicTest(String name){this.name = name;}
In diesem Fall ändere ich doch den Wert bzw. die Referenz der als final deklarierten Variable. Was ja eigentlich nicht gehen dürfte, da das Attribut ja schon mit null belegt ist. Bin momentan etwas verwirrt. Evtl. kann mir jemand die Frage beantworten.
Du kannst den Wert genau einmal setzen (einmal initialisieren), und der Compiler wird eine Fehlermeldung ausgeben wenn du auch nur ansatzweise versuchst die Variable zweimal zu ändern (probier es aus, schreib "this.name = ..." an mehr als einer Stelle hin).
Man muss diesen Wert sogar genau einmal setzen. Allerdings geht das auch nur in bestimmten Situationen. Bei Klassenvariablen ist dies entweder der Konstruktor oder Initializer. Es muss zur Compilezeit bewiesen werden können, dass die Variable durch jeden möglichen Pfad (if-else-Zweige, switch, etc.) genau einmal gesetzt wird.
genau einmal setzen. Das müsste mit null ja passieren und trotzdem kann ich, nachdem der Wert genau einmal auf null gesetzt wurde noch eine Referenz setzen.
Naja, Klassenvariablen sind per Default null/0/0.0/false, das stimmt schon. Das mit final ist eben ein Sonderfall. Initialisierung und Zuweisung sind in der JVM zwei Verschiedene Dinge. final erzwingt eine Zuweisung, die über die Initialisierung hinausgeht.
Nicht der Inhalt. Der Zustand des Objekts, das in einer finalen Variable gespeichert ist, lässt sich ändern.
[EDIT]ach du meinst nicht final variablen, okay. falsch verstanden[/EDIT]
also mein compiler streikt wen ich die nicht initialisiere
Code:
e:\w2\testarea\src>javac Test2.java
Test2.java:19: error: variable i might not have been initialized
System.out.println(i);
^
Test2.java:20: error: variable b might not have been initialized
System.out.println(b);
^
Test2.java:21: error: variable s might not have been initialized
System.out.println(s);
^
Test2.java:22: error: variable o might not have been initialized
System.out.println(o);
^
4 errors
Java:
publicclassTest2{privatefinalObject o;privatefinalint i;privatefinalboolean b;privatefinalString s;publicTest2(){System.out.println(i);System.out.println(b);System.out.println(s);System.out.println(o);}publicstaticvoidmain(String[] args)throwsIOException{Test2 test =newTest2();}
Ja, aber das ist was ganz anderes, als sie explizit auf 0 bzw. null zu setzen. Wenn du das mit den finalen Variablen machst, wird es nicht funktionieren.
private final String name; wird default mit null initialisiert. Jetzt muss ich aber eine Zuweisung einmalig vornehmen, da final eine Zuweisung erzwingt. Nach dieser Zuweisung (Referenz) kann ich keine neue Referenz zuweisen.
publicclassVariableTest{privatefinalString name;publicVariableTest(){System.out.println(this.name);
name ="name";}publicstaticvoidmain(finalString... args){newVariableTest();}}
Das liefert mir
Code:
null
.
Interessanterweise kriege ich einen Compilerfehler, wenn ich das
Code:
this
weglasse:
Java:
publicclassVariableTest{privatefinalString name;publicVariableTest(){System.out.println(name);// variable name might not have been initialized
name ="name";}publicstaticvoidmain(finalString... args){newVariableTest();}}
Gleiches Problem mit Klassenvariablen:
Java:
publicclassVariableTest{privatestaticfinalString name;static{System.out.println(VariableTest.name);
name ="name";}publicstaticvoidmain(finalString... args){}}
Liefert
Code:
null
.
Compilerfehler ohne
Code:
VariableTest
:
Java:
publicclassVariableTest{privatestaticfinalString name;static{System.out.println(name);// variable name might not have been initialized
name ="name";}publicstaticvoidmain(finalString... args){}}
Du hast meinen Beitrag missverstanden.
Es ging mir darum, zu zeigen, dass Instanz- und Klassenvariablen bei ihrer Erstellung mit einem Defaultwert (in diesem Fall
Code:
null
) initialisiert werden.
Die Zuweisung an name habe ich nur gemacht, um einen Compilerfehler zu verhindern.
Während ich an dem Code arbeitete, fiel mir auf, dass es einen Compilerfehler gibt ("variable name might not have been initialized"), wenn man nicht voll qualifiziert (
Code:
this.name
bei der Instanz- und
Code:
VariableTest.name
bei der Klassenvariable) auf die Variable zugreift. Eine Erklärung habe ich dafür bisher nicht gefunden.
IMHO sollte beides, also mit egal ob mit oder ohne this/Klassenname Qualifizierung, einen Compilerfehler erzeugen.
Der "Trick" an final ist u.a., dass bestimmte Annahmen gemacht werden können, zB. dass solche Variablen nicht uninitialisiert sein können (offensichtlich schon??) und dass alle Threads dasselbe zusehen bekommen.
Hmm... der Compiler kann aber natürlich nicht immer in allen Fällen überprüfen, ob gerade das aktuelle Objekt angesprochen wird.
Wenn der Compiler hier meckern würde:
Java:
publicclassVariableTest{privatefinalString name;publicVariableTest(){System.out.println(this.name);
name ="name";}publicstaticvoidmain(finalString... args){newVariableTest();}}
Sollte er dann nicht eigentlich auch hier meckern?
Java:
publicclassVariableTest{privatefinalString name;publicVariableTest(){VariableTest foo =this;System.out.println(foo.name);
name ="name";}publicstaticvoidmain(finalString... args){newVariableTest();}}
Und was ist dann damit?
Java:
publicclassVariableTest{privatefinalString name;publicVariableTest(){VariableTest foo =lengthyComputationWhichAlwaysReturnsTrue()?this:null;System.out.println(foo.name);
name ="name";}publicstaticvoidmain(finalString... args){newVariableTest();}}
Nebenbei werden die Variablem bei erzeugen des Objekts mit Defaults initialisiert. Und das passiert beim Konstruktoraufruf (einfach mal debuggen, da sieht man es)
Final-Variablen werden auch nicht per Default mit null initialisiert sondern einmalig auf den Wert gesetzt bei der Zuweisung.
Die Variablen werden schon vor dem Aufruf des Konstruktors/Instanzinitialisierers mit Defaultwerten initialisiert. Unabhängig davon, ob sie
Code:
final
sind oder nicht. Das haben die Ausgabe des Testcodes und auch der Debugger bei mir gezeigt.
Dazu dann auch ein Zitat aus der JVMS:
Memory for a new instance of that class is allocated from the garbage-collected heap, and the instance variables of the new object are initialized to their default initial values (§2.3, §2.4).
[…]
The new instruction does not completely create a new instance; instance creation is not completed until an instance initialization method (§2.9) has been invoked on the uninitialized instance.
Hmm... der Compiler kann aber natürlich nicht immer in allen Fällen überprüfen, ob gerade das aktuelle Objekt angesprochen wird.
Wenn der Compiler hier meckern würde:
...snip...
Sollte er dann nicht eigentlich auch hier meckern?
...snip...
Und was ist dann damit?
...snip...
In den ersten beiden Fällen sollte der Compiler eigentlich alle Informationen haben (beim Dritten scheiterts am Halteproblem). Über eine Datenflussanalyse sollte das eigentlich trivial sein. Allerdings zeigen sowohl javac als auch der JDT Compiler das gleiche Verhalten, was darauf schließen lässt, dass es wahrscheinlich aus der Spec hervorgeht. Ich habe jetzt allerdings eine ganze Weile danach gesucht (mir klingelt gerade der Kopf vor lauter "definite assignment" etc.) aber ich habe nichts gefunden. Hast Du ne Idee, woraus das hervorgehen könnte?
final (Java) - Wikipedia, the free encyclopedia
Ich glaube dieses Verhalten von final Variablen ist beabsichtigt. Das du mit this. auf sie zugreifen kannst könnte wohl ein (nicht wirklich schlimmer) Bug sein.
Man kann den Compilerfehler ebenfalls über eine Methode umgehen:
Java:
publicclassVariableTest{privatefinalString name;privateVariableTest(){name();
name ="name";name();}privatevoidname(){System.out.println(name);// liefert trotz final nicht die gleiche Ausgabe}publicstaticvoidmain(finalString... args){newVariableTest();}}