假设我们有以下类:
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); } }
现在让我们recursive
在A班打电话:
public class Demo { public static void main(String[] args) { A a = new A(); a.recursive(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)
让我们来看看令人困惑的部分.现在我们打电话给recursive
B班.
预期:
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...
这是怎么发生的?我知道这是一个设计的例子,但它让我很奇怪.
具体用例的旧问题.
这是预料之中的.这是一个实例发生的事情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
将在被称为递归函数交替.
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()
,解析为覆盖B
.我们来回打乒乓直到宇宙结束或者以StackOverflowError
先到者为准.
这将是很好,如果能写this.recursive(i-1)
在A
拿到自己的实现,但是这可能会打破东西,并有其他不幸的后果,所以this.recursive(i-1)
在A
所调用B.recursive()
等等.
有一种方法可以获得预期的行为,但它需要先见之明.换句话说,您必须提前知道您希望super.recursive()
在实现中A
陷入被困的子类型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
调用父类的方法明确,所以recursive
中A
被调用一次.
然后,recursive(i - 1);
在类A中将调用recursive
类B
中覆盖recursive
类的方法A
,因为它是在类的实例上执行的B
.
随后B
的recursive
会叫A
的recursive
明确,等等.
这实际上不能采取任何其他方式.
当你打电话B.recursive(10);
,然后打印B.recursive(10)
然后调用此方法的实现A
用i+1
.
所以你打电话A.recursive(11)
,打印A.recursive(11)
它调用recursive(i-1);
的当前实例是方法B
与输入参数i-1
,所以它调用B.recursive(10)
,然后调用超级实现与i+1
它11
,然后以递归方式调用当前实例是递归的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
没有进一步限定的情况下调用时,那就是子类实现.结果就是你所看到的永无止境的循环.