継承による再帰のトレース

継承を使うと、元のメソッドを変えずに、メソッドの再帰呼び出しの引数および戻り値を見ることができることに気づいた。


再帰による階乗の計算をstaticでないfactorialメソッドとして持つクラス
Factorialがあるとする。

public class Factorial {
	public int factorial(int n){
		if(n <= 0){
			return 1;
		}
		return n * factorial(n - 1);
	}
	
	public static void main(String[] args) {
		Factorial f = new Factorial();
		int value = f.factorial(10);
		System.out.println("result: " + value);
	}
}

factorialを呼びだす側では、一度factorialを呼びだしてしまうと最初の呼び出しがreturnされるまでに命令を挟むことができない。したがって呼び出される再帰メソッドfactorialを書き換えなければ引数、戻り値を見ることができない…と考えていたのだが。

継承によるトレース

Factorialを継承して、factorialメソッドをオーバーライドする。
すると、super.factorialの呼び出しの前後で、引数および戻り値を見ることができる。

public class SubFactorial extends Factorial{

	@Override
	public int factorial(int n) {
		//引数
		System.out.println("called with arg: " + n);

		int value = super.factorial(n);

		//戻り値
		System.out.println("returned: " + value);
		return value;
	}
	
	public static void main(String[] args) {
		SubFactorial sf = new SubFactorial();
		int value = sf.factorial(10);
		System.out.println("result: " + value);
	}
}

これでメソッド呼び出し、returnの順序が、

call sub(10) -> call f(10) -> call sub(9) -> call f(9) -> ... -> call f(0) ->
return f(0) -> return sub(0) -> ... return f(10) -> return sub(10)

となるため、SubFactorial.factorialの中では、fの呼び出しの際の引数、returnの際の戻り値がその都度見えることになる。

実行結果は以下のようになる。

called with arg: 10
called with arg: 9
called with arg: 8
called with arg: 7
called with arg: 6
called with arg: 5
called with arg: 4
called with arg: 3
called with arg: 2
called with arg: 1
called with arg: 0
returned: 1
returned: 1
returned: 2
returned: 6
returned: 24
returned: 120
returned: 720
returned: 5040
returned: 40320
returned: 362880
returned: 3628800
result: 3628800

10の階乗は360万くらい。

Fibonacciでもやってみた。

public class Fibonacci {
	public int fib(int n){
		//fib(0) = fib(1) = 1
		if (n <= 1){
			return 1;
		}
		//n >= 2
		return fib(n-1) + fib(n-2);
	}
}

見にくかったのでスペース挿入。

public class SubFibonacci extends Fibonacci{

	@Override
	public int fib(int n) {
		//引数
		for (int i = 0; i < n; i++) {
			System.out.print("  ");
		}
		System.out.println("called with arg: " + n);
		
		int value = super.fib(n);
		
		//戻り値
		for (int i = 0; i < n; i++) {
			System.out.print("  ");
		}
		System.out.println("returned: " + value);
		return value;
	}
	
	public static void main(String[] args) {
		SubFibonacci sf = new SubFibonacci();
		int value = sf.fib(5);
		System.out.println("result: " + value);
	}
}

結果はこんなん。

          called with arg: 5
        called with arg: 4
      called with arg: 3
    called with arg: 2
  called with arg: 1
  returned: 1
called with arg: 0
returned: 1
    returned: 2
  called with arg: 1
  returned: 1
      returned: 3
    called with arg: 2
  called with arg: 1
  returned: 1
called with arg: 0
returned: 1
    returned: 2
        returned: 5
      called with arg: 3
    called with arg: 2
  called with arg: 1
  returned: 1
called with arg: 0
returned: 1
    returned: 2
  called with arg: 1
  returned: 1
      returned: 3
          returned: 8
result: 8