Supongamos que tenemos las siguientes clases:
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);
}
}
Ahora llamemos recursive
a la clase A:
public class Demo {
public static void main(String[] args) {
A a = new A();
a.recursive(10);
}
}
La salida es, como se esperaba, contando hacia atrás desde 10.
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)
Vayamos a la parte confusa. Ahora llamamos recursive
a la clase B.
Esperado :
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)
Actual :
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
A.recursive(11)
B.recursive(10)
..infinite loop...
¿Como sucedió esto? Sé que este es un ejemplo inventado, pero me hace preguntarme.
Pregunta anterior con un caso de uso concreto .
java
inheritance
recursion
raupach
fuente
fuente
A
realidad se envía dinámicamente alrecursive
método del objeto actual. Si está trabajando con unA
objeto, la llamada lo lleva aA.recursive()
, y con unB
objeto, aB.recursive()
. PeroB.recursive()
siempre llamaA.recursive()
. Entonces, si inicia unB
objeto, cambia de un lado a otro.Respuestas:
Se esperaba esto. Esto es lo que sucede con una instancia de
B
.class A { void recursive(int i) { // <-- 3. this gets called System.out.println("A.recursive(" + i + ")"); if (i > 0) { recursive(i - 1); // <-- 4. this calls the overriden "recursive" method in class B, going back to 1. } } } class B extends A { void recursive(int i) { // <-- 1. this gets called System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1); // <-- 2. this calls the "recursive" method of the parent class } }
Como tal, las llamadas se alternan entre
A
yB
.Esto no sucede en el caso de una instancia de
A
porque no se llamará al método anulado.fuente
Porque
recursive(i - 1);
en seA
refiere athis.recursive(i - 1);
cuál estáB#recursive
en segundo caso. Entonces,super
ythis
se llamará alternativamente en función recursiva .void recursive(int i) { System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1);//Method of A will be called }
en
A
void recursive(int i) { System.out.println("A.recursive(" + i + ")"); if (i > 0) { this.recursive(i - 1);// call B#recursive } }
fuente
Todas las otras respuestas han explicado el punto esencial, que una vez que se anula un método de instancia, permanece anulado y no se puede recuperar excepto a través
super
.B.recursive()
invocaA.recursive()
.A.recursive()
luego invocarecursive()
, que se resuelve en la invalidaciónB
. Y hacemos ping pong de un lado a otro hasta el fin del universo o unStackOverflowError
, lo que ocurra primero.Sería bueno si se podría escribir
this.recursive(i-1)
enA
conseguir su propia implementación, pero que probablemente romper cosas y tienen otras consecuencias desafortunadas, por lo quethis.recursive(i-1)
enA
invocaB.recursive()
y así sucesivamente.Hay una forma de obtener el comportamiento esperado, pero requiere previsión. En otras palabras, debe saber de antemano que desea que
super.recursive()
un subtipo deA
quede atrapado, por así decirlo, en laA
implementación. Se hace así:class A { void recursive(int i) { doRecursive(i); } private void doRecursive(int i) { System.out.println("A.recursive(" + i + ")"); if (i > 0) { doRecursive(i - 1); } } } class B extends A { void recursive(int i) { System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1); } }
Dado que
A.recursive()
invocadoRecursive()
ydoRecursive()
nunca se puede invalidar,A
se asegura que está llamando a su propia lógica.fuente
doRecursive()
interiorrecursive()
desde el objetoB
. Como TAsk escribió en su respuesta, una llamada a función funciona comothis.doRecursive()
y ObjectB
(this
) no tiene un métododoRecursive()
porque está en la claseA
definida comoprivate
y noprotected
y, por lo tanto, no se heredará, ¿verdad?B
no puede llamardoRecursive()
en absoluto.doRecursive()
esprivate
, si. Pero cuandoB
llamasuper.recursive()
, eso invoca la implementación derecursive()
inA
, que tiene acceso adoRecursive()
.super.recursive(i + 1);
en la claseB
llama al método de la superclase explícita, de modorecursive
deA
que se llama una vez.Entonces,
recursive(i - 1);
en la clase A llamaría alrecursive
método en la claseB
que anula larecursive
claseA
, ya que se ejecuta en una instancia de la clase.B
.Luego
B
'srecursive
llamaríanA
' srecursive
de forma explícita, y así sucesivamente.fuente
Eso en realidad no puede ser de otra manera.
Cuando llama
B.recursive(10);
, imprime yB.recursive(10)
luego llama a la implementación de este métodoA
coni+1
.Entonces llama
A.recursive(11)
, que imprimeA.recursive(11)
cuál llama alrecursive(i-1);
método en la instancia actual que estáB
con el parámetro de entradai-1
, por lo que llamaB.recursive(10)
, que luego llama a la súper implementación con lai+1
cual es11
, que luego llama recursivamente a la instancia actual recursiva con lai-1
cual es10
, y usted obtén el bucle que ves aquí.Todo esto se debe a que si llama al método de la instancia en la superclase, aún llamará a la implementación de la instancia en la que la está llamando.
Imagina esto,
public abstract class Animal { public Animal() { makeSound(); } public abstract void makeSound(); } public class Dog extends Animal { public Dog() { super(); //implicitly called } @Override public void makeSound() { System.out.println("BARK"); } } public class Main { public static void main(String[] args) { Dog dog = new Dog(); } }
Obtendrá "BARK" en lugar de un error de compilación como "el método abstracto no se puede llamar en esta instancia" o un error de tiempo de ejecución
AbstractMethodError
o inclusopure virtual method call
o algo así. Así que todo esto es para apoyar el polimorfismo .fuente
Cuando
B
elrecursive
método de una instancia llama a lasuper
implementación de la clase, la instancia sobre la que se actúa sigue siendo deB
. Por lo tanto, cuando la implementación de la superclase llamarecursive
sin más calificación, esa es la implementación de la subclase . El resultado es el bucle sin fin que está viendo.fuente