inheritance和recursion
假设我们有以下类:
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); } }
现在让我们在A类中调用recursive
:
public class Demo { public static void main(String[] args) { A a = new A(); a.recursive(10); } }
如预期的那样,产量从10下降到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)
让我们来看看令人困惑的部分。 现在我们在B类中调用recursive
预期 :
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)
实际 :
B.recursive(10) A.recursive(11) B.recursive(10) A.recursive(11) B.recursive(10) A.recursive(11) B.recursive(10) ..infinite loop...
这是怎么发生的? 我知道这是一个devise的例子,但它让我感到惊讶。
较老的问题与一个具体的用例 。
这是预料之中的。 这是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 } }
因此,呼叫在A
和B
之间交替。
在A
的实例中不会发生这种情况,因为不会调用overriden方法。
因为recursive(i - 1);
在A
是指this.recursive(i - 1);
这是第二种情况下的B#recursive
。 所以, super
和this
将被称为交替 recursion函数。
void recursive(int i) { System.out.println("B.recursive(" + i + ")"); super.recursive(i + 1);//Method of A will be called }
在A
void recursive(int i) { System.out.println("A.recursive(" + i + ")"); if (i > 0) { this.recursive(i - 1);// call B#recursive } }
其他的答案都解释了要点,一旦一个实例方法被覆盖,它将被覆盖,除非通过super
否则没有得到它。 B.recursive()
调用A.recursive()
。 A.recursive()
然后调用recursive()
,它parsing为B
的覆盖。 我们来回乒乓直到宇宙结束或StackOverflowError
,以先到者为准。
如果可以在A
编写this.recursive(i-1)
以获得自己的实现,那将是很好的,但是这可能会破坏事物并且具有其他不幸的后果,所以在A
this.recursive(i-1)
调用B.recursive()
等等。
有一种方法可以获得预期的行为,但这需要先见之明。 换句话说,你必须事先知道你想在A
一个子types中的super.recursive()
被捕获,也就是说,在A
实现中。 它是这样做的:
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); } }
由于A.recursive()
调用doRecursive()
和doRecursive()
永远不能被覆盖,所以A
保证调用它自己的逻辑。
super.recursive(i + 1);
在B
类中显式调用超类的方法,所以A
recursive
被调用一次。
然后, recursive(i - 1);
在类A中会调用类B
的recursive
方法,因为它是在类B
一个实例上执行的,所以它将覆盖类A
recursive
。
那么B
的recursive
会明确地调用A
的recursive
,依此类推。
这实际上不能走任何其他的方式。
当你调用B.recursive(10);
,然后打印B.recursive(10)
然后用i+1
调用A
的这个方法的实现。
所以你调用A.recursive(11)
,它打印A.recursive(11)
,它调用recursive(i-1);
方法在当前实例B
上input参数i-1
,因此它调用B.recursive(10)
,然后调用超级实现与i+1
是11
,然后recursion调用当前实例recursion与i-1
这是10
,你会得到你在这里看到的循环。
这是因为如果你调用超类中的实例的方法,你仍然会调用你调用它的实例的实现。
想象一下,
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(); } }
你会得到“BARK”而不是一个编译错误,比如“在这个实例上不能调用抽象方法”,或者是一个运行时错误AbstractMethodError
,甚至是pure virtual method call
或者类似的东西。 所以这都是为了支持多态 。
当B
实例的recursive
方法调用super
类实现时,正在执行的实例仍然是B
因此,当超类的实现没有进一步的限定调用recursive
, 这就是子类的实现 。 结果是你看到的永无止境的循环。