Warum werden Felder nicht auf nicht standardmäßige Werte initialisiert, wenn eine Methode von super () ausgeführt wird?

8

Ich muss mehr als eine Stunde damit verbracht haben, den Grund für ein unerwartetes Verhalten herauszufinden. Ich stellte schließlich fest, dass ein Feld nicht so eingestellt war, wie ich es erwarten würde. Bevor ich zuhöre und weitermache, würde ich gerne verstehen, warum das so funktioniert.

Beim Ausführen des folgenden Beispiels würde ich erwarten, dass die Ausgabe wahr ist, aber sie ist falsch. Andere Tests zeigen, dass ich immer den Standardwert dieses Typs erhalte.

%Vor%

Ausgabe:

%Vor%     
Smig 15.07.2013, 15:25
quelle

5 Antworten

7
%Vor%

ist identisch mit

%Vor%

Der Compiler verschiebt die Initialisierungen der Felder automatisch innerhalb des Konstruktors (direkt nach dem Aufruf des Superkonstruktors, implizit oder explizit).

Da der Standardwert eines booleschen Feldes false ist, wenn super() aufgerufen wird (und somit ClassOne() und fireMethod() ), wurde bool noch nicht auf true gesetzt.

Fun Tatsache: der folgende Konstruktor

%Vor%

wird als

verstanden %Vor%

von der JVM, und die Ausgabe wird somit

sein %Vor%     
sp00m 15.07.2013, 15:32
quelle
5

Der Superklassenkonstruktor wird vor dem Unterklassenkonstruktor aufgerufen. Und bevor ein Konstruktor ausgeführt wird, haben alle Instanzmitglieder in Java ihren Standardwert (false, 0, null). Wenn also super() aufgerufen wird, ist bool immer noch falsch (Standardwert für boolesche Werte).

Ganz allgemein ist das Aufrufen einer überschreibbaren Methode aus einem Konstruktor (in ClassOne) eine schlechte Idee für den Grund, den Sie gerade entdeckt haben: Sie könnten am Ende an einem Objekt arbeiten, das noch nicht vollständig initialisiert wurde.

    
assylias 15.07.2013 15:29
quelle
1

Die Instanzinitialisierungen werden ausgeführt, nachdem super () implizit oder explizit aufgerufen wurde.

Aus der Java-Sprachspezifikation Abschnitt 12.5: "Erstellung von neuen Klasseninstanzen :

  

"3. Dieser Konstruktor beginnt nicht mit einem expliziten Konstruktoraufruf von   ein anderer Konstruktor in der gleichen Klasse (mit diesem). Wenn dieser Konstruktor für   eine andere Klasse als Object, dann beginnt dieser Konstruktor mit einem Explicit   oder impliziter Aufruf eines Superklassenkonstruktors (mit super). Bewerten   Argumente und verarbeiten diesen Superklassenkonstruktoraufruf rekursiv unter Verwendung derselben fünf Schritte. Wenn dieser Aufruf des Konstruktors abrupt abgeschlossen wird, dann   Dieser Vorgang wird aus dem gleichen Grund abrupt beendet. Ansonsten, fahre fort   mit Schritt 4.

     

"4. Führen Sie die Instanzinitialisierungen und Instanzvariableninitialisierungen für diese Klasse aus.   Zuweisen der Werte der Instanzvariableninitialisierer zu den entsprechenden   Instanzvariablen in der Reihenfolge von links nach rechts, in der sie textuell erscheinen   der Quellcode für die Klasse. Wenn die Ausführung eines dieser Initialisierer erfolgt   In einer Ausnahme werden dann keine weiteren Initialisierer und diese Prozedur verarbeitet   schließt abrupt mit derselben Ausnahme ab. Fahren Sie andernfalls mit Schritt 5 fort. "

    
Andy Thomas 15.07.2013 15:29
quelle
0

super() ist in diesem Fall eigentlich überflüssig (Wortspiel nicht beabsichtigt), weil es implizit in jedem Konstruktor aufgerufen wird. Was das bedeutet ist, dass der Konstruktor von ClassOne zuerst aufgerufen wird. Bevor also ein Konstruktor ausgeführt wird, haben die Instanzmitglieder ihre Standardwerte (also bool ist false ). Es ist nur nach die Konstruktoren ausgeführt werden, dass die Felder initialisiert werden.

Ihr Konstruktor wird also effektiv:

%Vor%

Aber ClassOne ruft die überschreibbare Methode auf, die den Wert von bool ausgibt, was an diesem Punkt false ist.

Im Allgemeinen ist es eine schlechte Methode, überschreibbare Methoden von einem Konstruktor aus aufzurufen (wie Sie es in ClassOne tun), weil Sie jetzt mit einem Objekt arbeiten, das nicht vollständig initialisiert ist.

Aus effektivem Java (2. Ausgabe):

  

Es gibt einige weitere Einschränkungen, die eine Klasse erfüllen muss, um Vererbung zuzulassen. Konstruktoren dürfen keine überschreibbaren Methoden direkt oder indirekt aufrufen. Wenn Sie diese Regel verletzen, führt dies zu einem Programmfehler. Der Superklassenkonstruktor wird vor dem Unterklassenkonstruktor ausgeführt, sodass die überschreibende Methode in der Unterklasse aufgerufen wird, bevor der Unterklassenkonstruktor ausgeführt wurde. Wenn die überschreibende Methode von einer vom Unterklassenkonstruktor durchgeführten Initialisierung abhängt, verhält sich die Methode nicht wie erwartet.

    
Vivin Paliath 15.07.2013 15:31
quelle
0

Die endgültige Antwort lautet: Verwenden Sie keine überschreibbare Methode in einem Konstruktor.

In jedem Konstruktor:

  • Rufen Sie den Superkonstruktor (implizit oder zuerst im Konstruktor)
  • auf
  • Führen Sie die Feldinitialisierungen der aktuellen Klasse ( type field = value; )
  • aus
  • Mach den Rest des Konstruktors

Das macht das Leben interessant

%Vor%

Dies führt zu

%Vor%
  1. Wie beim Aufruf von B.init B sind Felder die Nullstellen.
  2. Es wird a auf 7 (für nichts) und b auf 15 gesetzt.
  3. Dann wird in B's Konstruktor a initialisiert.

Meine IDE markiert bereits das Aufrufen einer überschreibbaren Methode in einem Konstruktor als schlechten Stil.

    
Joop Eggen 15.07.2013 15:44
quelle

Tags und Links