Angenommen, wir haben die folgenden Klassen:
class A {
void recursive(int i) {
System.out.println("A.recursive(" + i + ")");
if (i > 0) {
recursive(i - 1);
}
}
}
class B extends A {
void recursive(int i) {
System.out.println("B.recursive(" + i + ")");
super.recursive(i + 1);
}
}
Rufen wir jetzt recursive
Klasse A an:
public class Demo {
public static void main(String[] args) {
A a = new A();
a.recursive(10);
}
}
Die Ausgabe wird erwartungsgemäß von 10 heruntergezählt.
A.recursive(10)
A.recursive(9)
A.recursive(8)
A.recursive(7)
A.recursive(6)
A.recursive(5)
A.recursive(4)
A.recursive(3)
A.recursive(2)
A.recursive(1)
A.recursive(0)
Kommen wir zum verwirrenden Teil. Jetzt rufen wir recursive
Klasse B an.
Erwartet :
B.recursive(10)
A.recursive(11)
A.recursive(10)
A.recursive(9)
A.recursive(8)
A.recursive(7)
A.recursive(6)
A.recursive(5)
A.recursive(4)
A.recursive(3)
A.recursive(2)
A.recursive(1)
A.recursive(0)
Tatsächlich :
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
..infinite loop...
Wie kommt es dazu? Ich weiß, dass dies ein erfundenes Beispiel ist, aber ich wundere mich darüber.
Ältere Frage mit einem konkreten Anwendungsfall .
java
inheritance
recursion
Björn Raupach
quelle
quelle
A
tatsächlich dynamisch an dierecursive
Methode des aktuellen Objekts gesendet wird. Wenn Sie mit einemA
Objekt arbeiten, führt Sie der Aufruf zuA.recursive()
und mit einemB
Objekt zuB.recursive()
. Ruft aberB.recursive()
immer anA.recursive()
. Wenn Sie also einB
Objekt starten , wechselt es hin und her.Antworten:
Dies wird erwartet. Dies ist, was für eine Instanz von passiert
B
.Daher wechseln die Anrufe zwischen
A
undB
.Dies ist bei einer Instanz von nicht der Fall,
A
da die Überschreibungsmethode nicht aufgerufen wird.quelle
Denn
recursive(i - 1);
inA
bezieht sich aufthis.recursive(i - 1);
wasB#recursive
im zweiten Fall ist. Alsosuper
undthis
wird alternativ in rekursiver Funktion aufgerufen .im
A
quelle
Die anderen Antworten haben alle den wesentlichen Punkt erklärt: Sobald eine Instanzmethode überschrieben wird, bleibt sie überschrieben und kann nur durch zurückgegeben werden
super
.B.recursive()
ruft aufA.recursive()
.A.recursive()
ruft dann aufrecursive()
, was zum Überschreiben in auflöstB
. Und wir ping pong hin und her bis zum Ende des Universums oder aStackOverflowError
, je nachdem, was zuerst eintritt.Es wäre schön, wenn man schreiben könnte
this.recursive(i-1)
inA
seine eigene Implementierung, aber das würde wahrscheinlich brechen Dinge und hat andere unangenehme Folgen, sothis.recursive(i-1)
inA
aufrufenB.recursive()
und so weiter.Es gibt einen Weg, um das erwartete Verhalten zu erreichen, aber es erfordert Voraussicht. Mit anderen Worten, Sie müssen im Voraus wissen, dass
super.recursive()
ein Subtyp vonA
sozusagen in derA
Implementierung eingeschlossen werden soll. Es wird so gemacht:Da
A.recursive()
aufgerufen wirddoRecursive()
unddoRecursive()
niemals überschrieben werden kann,A
wird sichergestellt, dass es seine eigene Logik aufruft.quelle
doRecursive()
innen zu rufen . Wie TAsk in seiner Antwort schrieb, funktioniert ein Funktionsaufruf wie folgt und Object ( ) hat keine Methode, da es in der Klasse als und nicht definiert ist und daher nicht vererbt wird, oder?recursive()
B
this.doRecursive()
B
this
doRecursive()
A
private
protected
B
kann überhaupt nicht aufrufendoRecursive()
.doRecursive()
istprivate
ja. BeiB
Aufrufensuper.recursive()
ruft dies jedoch die Implementierung vonrecursive()
in aufA
, auf das zugegriffen werden kanndoRecursive()
.super.recursive(i + 1);
in classB
ruft die Methode der Superklasse explizit auf, alsorecursive
vonA
einmal aufgerufen wird.Dann
recursive(i - 1);
A in der Klasse würde der Aufrufrecursive
Methode in der Klasse ,B
die Vorrang vorrecursive
der KlasseA
, da sie auf eine Instanz der Klasse ausgeführt wirdB
.Dann
B
‚srecursive
nennen würdeA
‘ srecursive
ausdrücklich, und so weiter.quelle
Das kann eigentlich nicht anders gehen.
Wenn Sie aufrufen
B.recursive(10);
, wird gedrucktB.recursive(10)
und die Implementierung dieser MethodeA
mit aufgerufeni+1
.Sie rufen also auf
A.recursive(11)
, was druckt,A.recursive(11)
was dierecursive(i-1);
Methode für die aktuelle InstanzB
mit dem Eingabeparameteri-1
aufruftB.recursive(10)
, und ruft auf , was dann die Super-Implementierung aufruft , miti+1
der is ist11
, und die dann rekursiv die aktuelle Instanz aufruft, miti-1
der is ist10
, und Sie werden Holen Sie sich die Schleife, die Sie hier sehen.Dies liegt daran, dass Sie beim Aufrufen der Methode der Instanz in der Oberklasse immer noch die Implementierung der Instanz aufrufen, auf der Sie sie aufrufen.
Stell dir das vor,
Sie erhalten "BARK" anstelle eines Kompilierungsfehlers wie "Die abstrakte Methode kann in dieser Instanz nicht aufgerufen werden" oder eines Laufzeitfehlers
AbstractMethodError
oder sogarpure virtual method call
oder so ähnlich. Das alles soll also den Polymorphismus unterstützen .quelle
Wenn
B
dierecursive
Methode einer Instanz diesuper
Klassenimplementierung aufruft , ist die Instanz, auf die reagiert wird, immer noch vonB
. Wenn die Implementierung der Superklasserecursive
ohne weitere Qualifikation aufgerufen wird , ist dies die Implementierung der Unterklasse . Das Ergebnis ist die Endlosschleife, die Sie sehen.quelle