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.
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()
...
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
undclone
. (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.
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
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.
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.