[java] 상속 및 재귀

다음 클래스가 있다고 가정합니다.

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재정의 된 메서드가 호출되지 않기 때문에 인스턴스의 경우에는 발생하지 않습니다 .


답변

때문에 recursive(i - 1);In이 A참조하는 this.recursive(i - 1);B#recursive두 번째 경우. 그래서, superthis호출 할 것이다 재귀 함수를 다른 방법 .

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 AA구현 에서 트랩 되기를 원한다는 것을 미리 알아야합니다 . 다음과 같이 수행됩니다.

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);in class B는 명시 적으로 슈퍼 클래스의 메서드를 호출하므로 recursiveof A는 한 번 호출됩니다.

그런 다음 recursive(i - 1);클래스 A recursive에서 클래스 의 인스턴스에서 실행되기 때문에 클래스 B를 재정의 recursive하는 클래스 의 메서드를 호출합니다 .AB

그런 다음 B‘s recursiveA‘s를 recursive명시 적으로 호출 합니다.


답변

그것은 실제로 다른 방법으로 갈 수 없습니다.

를 호출 B.recursive(10);하면 인쇄 B.recursive(10)한 다음 Awith 에서이 메서드의 구현을 호출합니다 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추가 자격없이 호출 될 때 이것이 하위 클래스 구현 입니다. 결과는 당신이보고있는 끝없는 루프입니다.


답변