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 recursivea 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 recursivea 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

Arealidad se envía dinámicamente alrecursivemétodo del objeto actual. Si está trabajando con unAobjeto, la llamada lo lleva aA.recursive(), y con unBobjeto, aB.recursive(). PeroB.recursive()siempre llamaA.recursive(). Entonces, si inicia unBobjeto, 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
AyB.Esto no sucede en el caso de una instancia de
Aporque no se llamará al método anulado.fuente
Porque
recursive(i - 1);en seArefiere athis.recursive(i - 1);cuál estáB#recursiveen segundo caso. Entonces,superythisse 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
Avoid 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)enAconseguir su propia implementación, pero que probablemente romper cosas y tienen otras consecuencias desafortunadas, por lo quethis.recursive(i-1)enAinvocaB.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 deAquede atrapado, por así decirlo, en laAimplementació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,Ase 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 claseAdefinida comoprivatey noprotectedy, por lo tanto, no se heredará, ¿verdad?Bno puede llamardoRecursive()en absoluto.doRecursive()esprivate, si. Pero cuandoBllamasuper.recursive(), eso invoca la implementación derecursive()inA, que tiene acceso adoRecursive().super.recursive(i + 1);en la claseBllama al método de la superclase explícita, de modorecursivedeAque se llama una vez.Entonces,
recursive(i - 1);en la clase A llamaría alrecursivemétodo en la claseBque anula larecursiveclaseA, ya que se ejecuta en una instancia de la clase.B.Luego
B'srecursivellamaríanA' srecursivede 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étodoAconi+1.Entonces llama
A.recursive(11), que imprimeA.recursive(11)cuál llama alrecursive(i-1);método en la instancia actual que estáBcon el parámetro de entradai-1, por lo que llamaB.recursive(10), que luego llama a la súper implementación con lai+1cual es11, que luego llama recursivamente a la instancia actual recursiva con lai-1cual 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
AbstractMethodErroro inclusopure virtual method callo algo así. Así que todo esto es para apoyar el polimorfismo .fuente
Cuando
Belrecursivemétodo de una instancia llama a lasuperimplementació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 llamarecursivesin más calificación, esa es la implementación de la subclase . El resultado es el bucle sin fin que está viendo.fuente