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

因此,呼叫在AB之间交替。

A的实例中不会发生这种情况,因为不会调用overriden方法。

因为recursive(i - 1);A是指this.recursive(i - 1); 这是第二种情况下的B#recursive 。 所以, superthis将被称为交替 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中会调用类Brecursive方法,因为它是在类B一个实例上执行的,所以它将覆盖类A recursive

那么Brecursive会明确地调用Arecursive ,依此类推。

这实际上不能走任何其他的方式。

当你调用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+111 ,然后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这就是子类的实现 。 结果是你看到的永无止境的循环。