Gibt es eine Möglichkeit, Membervariablen einer Unterklasse in Java zu initialisieren, bevor der Konstruktor der Oberklasse aufgerufen wird?

8

Ich brauche das, weil der Konstruktor in der Oberklasse eine Methode aufruft, die in der Unterklasse überschrieben wird. Die Methode gibt einen Wert zurück, der an den Konstruktor der Unterklasse übergeben wird. Aber der Superklassenkonstruktor muss vor dem Unterklassenkonstruktor aufgerufen werden, so dass ich den übergebenen Wert nicht speichern kann.

    
kahoon 27.03.2010, 14:28
quelle

5 Antworten

17

Das Aufrufen einer überschriebenen Methode aus dem Superklassenkonstruktor wird einfach nicht funktionieren - tu es nicht. Der Superklassenkonstruktor muss immer vor dem der Unterklasse enden. Während der Superklassenkonstruktor ausgeführt wird, ist das fragliche Objekt eine (halb initialisierte) Instanz der Oberklasse, nicht die Unterklasse! Wenn Sie also versuchen, irgendeine überschriebene Funktion vom Konstruktor aus aufzurufen, werden die Unterklassenfelder, von denen sie abhängen kann, noch nicht initialisiert (genau wie Sie es beobachtet haben). Dies ist eine grundlegende Tatsache des Klassenentwurfs, und es gibt keine Problemumgehung.

Wie in Effektivem Java 2. Ed. (Kapitel 4, Punkt 17):

  

Es gibt [...] Einschränkungen, die eine Klasse erfüllen muss   Erbe. Konstruktoren dürfen keine überschreibbaren Methoden direkt aufrufen   oder indirekt. Wenn Sie diese Regel verletzen, führt dies zu einem Programmfehler. Das   Superklassenkonstruktor wird vor dem Unterklassenkonstruktor ausgeführt, also der   überschreibende Methode in der Unterklasse wird vor der Unterklasse aufgerufen   Konstruktor wurde ausgeführt. Wenn die überschreibende Methode von einer Initialisierung abhängig ist   Die Methode wird vom Unterklassenkonstruktor ausgeführt und verhält sich nicht wie   erwartet.

Wenn Sie die Superklassenimplementierung ändern können, versuchen Sie, den Aufruf an die virtuelle Funktion außerhalb des Konstruktors zu verschieben. Eine Möglichkeit, dies zu erreichen, ist die Verwendung einer Factory-Methode:

%Vor%

Beachten Sie, dass der Konstruktor von Subclass privat ist, um sicherzustellen, dass er nur über createInstance() instanziiert werden kann. Daher werden Instanzen immer ordnungsgemäß initialisiert. OTOH dies verhindert auch weitere Unterklassen. Eine Unterklassenbildung für eine konkrete Klasse wird jedoch ohnehin nicht empfohlen - eine Klasse, die unterkonzisiert werden soll, sollte abstrakt sein (mit einem protected -Konstruktor in diesem Fall). Und natürlich müssen alle weiteren Unterklassen auch nicht-öffentliche Konstruktoren und statische Factory-Methoden haben, die fleißig init() ...

aufrufen     
Péter Török 27.03.2010, 14:30
quelle
6

Ihre Frage wird im folgenden Code-Schnipsel zusammengefasst:

%Vor%

Dies ist ein Zitat von Josh Bloch und Neal Gafters Java Puzzlers: Traps, Fallstricke und Eckfälle :

  

Das Problem tritt auf, wenn ein Konstruktor eine Methode aufruft, die in ihrer Unterklasse überschrieben wurde. Eine auf diese Weise aufgerufene Methode wird immer ausgeführt, bevor die Instanz initialisiert wurde, wenn ihre deklarierten Felder noch ihre Standardwerte haben. Um dieses Problem zu vermeiden, rufen keine überschreibbaren Methoden von Konstruktoren direkt oder indirekt [EJ-Element 15] auf. Dieses Verbot erstreckt sich auf Instanzinitialisierer und die Körper der Pseudokonstruktoren readObject und clone . (Diese Methoden werden Pseudokonstruktoren genannt, weil sie Objekte erstellen, ohne einen Konstruktor aufzurufen.)

Kurz gesagt: TUN SIE ES NICHT!

Darüber hinaus schlägt das Buch eine mögliche Lösung vor (leicht für die Allgemeinheit bearbeitet):

  

Sie können das Problem beheben, indem Sie das Feld langsam initialisieren, wenn es zum ersten Mal verwendet wird, und nicht unbedingt, wenn die Instanz erstellt wird.

    
polygenelubricants 27.03.2010 14:44
quelle
1

Sie haben nicht erwähnt, ob Sie den Konstruktor der Oberklasse überhaupt ändern können. Wenn Sie können, dann ist dies machbar. Wie andere gesagt haben, ist dies nicht ratsam, aber hier ist ein künstliches Beispiel, das das tut, was Sie suchen - die Oberklasse ruft eine überschriebene Methode auf, die ein Argument verwendet, das an den Konstruktor der Unterklasse übergeben wurde:

%Vor%

& gt; Java abgeleitet von 25

    
Angus 04.04.2010 18:12
quelle
0

Ich kenne das Problem. Es ist nicht möglich. Wenn die Superklasse nicht deine eigene ist und du keine anderen Optionen hast, kannst du einige schlechte Hacks machen. Aber es wird nicht empfohlen.

Lösung 1:

Verwenden Sie eine statische Factory mit einer synchronisierten. Dies kann wie folgt aussehen (redudced):

%Vor%

Lösung 2:

Machen Sie eine Differenzierung in Ihrer Methode, wenn der eigene Konstruktor bereits mit einem Flag ausgeführt wurde. Dann müssen Sie einige Sachen des Superkonstruktors mit dem realen Wert duplizieren.

    
Horcrux7 27.03.2010 15:16
quelle
0

Wenn dies ein Problem ist, haben Sie ein grundlegendes Problem in Ihrer Klassenhierarchie; Superklassen können keine spezifische Unterklasse kennen, da es mehrere sein kann, sie könnten sogar dynamisch zur Laufzeit geladen werden. Deshalb müssen alle super () -Aufrufe in einem Konstruktor in der ersten Zeile stehen.

Rufen Sie in der Regel keine öffentlichen Methoden in einem Konstruktor auf.

    
Adriaan Koster 27.03.2010 17:59
quelle

Tags und Links