Inheritance and recursion
Suppose we have the following classes:
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);
}
}
Now lets call recursive
in class A:
public class Demo {
public static void main(String[] args) {
A a = new A();
a.recursive(10);
}
}
The output is, as expected counting down from 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)
Let's get to the the confusing part. Now we call recursive
in class B.
Expected:
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...
How does this happen? I know this is a devised example, but it makes me wonder.
Older question with a concrete use case.
Solution 1:
This is expected. This is what happens for an instance of 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
}
}
As such, the calls are alternating between A
and B
.
This doesn't happen in the case of an instance of A
because the overriden method won't be called.
Solution 2:
Because recursive(i - 1);
in A
refers to this.recursive(i - 1);
which is B#recursive
in second case. So, super
and this
will be called in recursive function alternatively.
void recursive(int i) {
System.out.println("B.recursive(" + i + ")");
super.recursive(i + 1);//Method of A will be called
}
in A
void recursive(int i) {
System.out.println("A.recursive(" + i + ")");
if (i > 0) {
this.recursive(i - 1);// call B#recursive
}
}
Solution 3:
The other answers have all explained the essential point, that once an instance method is overridden it stays overridden and there's no getting it back except through super
. B.recursive()
invokes A.recursive()
. A.recursive()
then invokes recursive()
, which resolves to the override in B
. And we ping pong back and forth until the end of the universe or a StackOverflowError
, whichever comes first.
It would be nice if one could write this.recursive(i-1)
in A
to get its own implementation, but that would probably break things and have other unfortunate consequences, so this.recursive(i-1)
in A
invokes B.recursive()
and so forth.
There is a way to get the expected behavior, but it requires foresight. In other words, you must know in advance that you want a super.recursive()
in a subtype of A
to get trapped, so to speak, in the A
implementation. It is done like so:
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);
}
}
Since A.recursive()
invokes doRecursive()
and doRecursive()
can never be overridden, A
is assured that it is calling its own logic.