次のクラスがあるとします:
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からカウントダウンされます。
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...
これはどのように起こりますか?これは考案された例であることは知っていますが、不思議に思います。
具体的なユースケース のある古い質問。
これは予想されることです。これは、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
のインスタンスの場合、オーバーライドされたメソッドは呼び出されないため、これは発生しません。
A
のrecursive(i - 1);
は、2番目のケースでは_B#recursive
_であるthis.recursive(i - 1);
を参照しているためです。したがって、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
に記述して独自の実装を取得できればいいのですが、それはおそらく物事を壊し、他の不幸な結果をもたらすので、A
のthis.recursive(i-1)
を呼び出しますB.recursive()
など。
予想される動作を取得する方法はありますが、先見性が必要です。言い換えれば、A
のサブタイプの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
は独自のロジックを呼び出していることが保証されます。
クラスB
のsuper.recursive(i + 1);
は、スーパークラスのメソッドを明示的に呼び出すため、recursive
のA
が1回呼び出されます。
次に、クラスAのrecursive(i - 1);
は、クラスrecursive
のB
をオーバーライドするクラスrecursive
のA
メソッドを呼び出します。クラスB
のインスタンスで実行されます。
次に、B
のrecursive
は、A
のrecursive
を明示的に呼び出します。
B
インスタンスのrecursive
メソッドがsuper
class実装を呼び出すとき、処理されるインスタンスはB
のままです。したがって、スーパークラスの実装がさらに修飾なしでrecursive
を呼び出すと、サブクラスの実装になります。結果は、あなたが見ている終わりのないループです。