같은 이름의 메서드지만 다르게 실행되는 이유

상곤·2025년 4월 14일

Java

목록 보기
11/22
post-thumbnail
class Parent {
    public void func1() {
        System.out.println("A");
    }

    public void func2() {
        System.out.print("B");
        func1();
    }
}
class Child extends Parent {
    public void func1() {
        System.out.print("D");
    }
    
    public void func2() {
        super.func2();
        System.out.print("C");
        func1();
    }
}

public class Test {
    public static void main(String[] args) {
        Parent a = new Child();
        a.func2();
        a.func1();
    }
}

이 때 결과는 어떻게 될까?

이 문제는 Java의 다형성, 메서드 오버라이딩, 그리고 super 키워드의 동작 방식을 정확히 이해하고 있는지를 묻는다.

결과는 BDCDD가 출력되며, 아래 세 가지 개념이 핵심이다.

  1. 동적 바인딩(Dynamic Binding)

    • 참조 변수의 타입이 Parent지만, 실제 객체가 Child라면 오버라이딩된 func1()이 실행된다.
  2. super 키워드 사용

    • Child의 func2() 안에서 super.func2()를 호출하면, Parent의 func2()가 실행된다.
  3. 메서드 오버라이딩(Method Overriding)

    • 부모 메서드 내에서 func1()를 호출해도, 실제 실행되는 건 자식 클래스의 func1()이다.

실행을 실제로 따라가면서 구체적으로 살펴보자

1. 객체 생성

public class Test {
    public static void main(String[] args) {
        Parent a = new Child();
        a.func2();
        a.func1();
    }
}

Parent a = new Child();이 부분은 업캐스팅(upcasting)이 사용된 부분이다.

참조를 하는 왼쪽의 참조 변수는 Parent 타입이지만, 실제로 참조할 객체는 Child인 상황이다.

이때 참조 변수 a는 Parent 타입으로 선언되어 있어서 Parent가 가진 멤버만 사용할 수 있지만,
실제 객체는 Child이기 때문에 메서드는 Child 기준으로 실행된다.
이처럼 부모가 자식을 참조하는 것을 업캐스팅이라 한다.

이렇게 외우면 쉽다. 부모는 자식을 품을 수 있지만, 자식은 부모를 품을 수 없다.

2. 동적 바인딩

public class Test {
    public static void main(String[] args) {
        Parent a = new Child();
        a.func2();
        a.func1();
    }
}

그 다음으로 a.func2(); 이 부분을 실행하면, Child 클래스의 func2()가 호출된다.

이 부분은 동적 바인딩 개념이 사용된 부분이다.
메서드를 호출할 때, JVM은 참조 변수의 타입이 아닌 실제 객체의 타입(Child)을 기준으로 어떤 메서드를 실행할지 결정한다.
실제로는 Child객체가 담겨있기에 Child 클래스에서 오버라이딩된 func2()가 호출되는 것이다.

3. super

    public void func2() {
        super.func2();
        System.out.print("C");
        func1();
    }

func2() 내부를 보면, super.func2()가 있다.

func2()는 오버라이딩된 메서드라서 그냥 호출하면 Child.func2()가 호출되지만,
이렇게 super 키워드를 사용하면 오버라이딩된 메서드가 아닌, 상속받은 부모 클래스의 원본 메서드가 호출된다.

    public void func2() {
        System.out.print("B");
        func1();
    }

그래서 이 부분이 실행되는 것이다.

그래서 System.out.print("B");가 실행되고, 처음 출력으로 찍히는 값은 B다.

그 다음 func1();가 실행된다.

이 때는 부모 객체에서 호출하고 있지만,
func1()도 오버라이딩된 상태이므로 다시 Child 클래스의 메서드가 호출된다.

func1()가 오버라이딩 되지 않았다면 Parent 클래스의 func1()이 호출된다.

    public void func1() {
        System.out.print("D");
    }

그래서 이 부분이 실행되고, 출력은 BD가 된다.

    public void func2() {
        super.func2();System.out.print("C");
        func1();
    }

여기까지가 super.func2();를 실행한 결과다.

이제 그 다음으로 출력이 BDC가 되고,
다시 오버라이딩된 func1();를 호출하면서 출력은 BDCD 가 된다.

그리고 여기까지가

public class Test {
    public static void main(String[] args) {
        Parent a = new Child();
        a.func2();
        a.func1();
    }
}

여기서

        Parent a = new Child();
        a.func2();

여기까지의 실행을 한 결과다.

이제 그 아래줄인 a.func1();을 실행하면 오버라이딩된 메서드가 실행되면서 출력은 최종적으로 BDCDD가 되는 것이다.

✔ 즉, 오버라이딩된 메서드호출 시점에 실제 객체의 타입을 기준으로 결정된다.
이것이 바로 자바의 다형성과 동적 바인딩의 핵심이다.

profile
🫠

0개의 댓글