자바에서 자식 클래스가 부모 클래스의 메서드를 오버라이딩하면, 컴파일 타임에는 메서드 호출이 참조 변수의 타입에 따라 결정되지만, 실행 시(런타임)에는 실제 객체의 타입에 따라 메서드가 호출된다. 이 과정은 자바의 중요한 기능인 다형성(polymorphism)과 동적 바인딩(dynamic binding)으로 이루어진다.
다형성은 하나의 참조 변수가 여러 타입의 객체를 참조할 수 있게 하여, 코드의 유연성을 높이는 객체지향 프로그래밍의 중요한 개념이다.
예를 들어, 부모 클래스 타입의 참조 변수로 자식 클래스 객체를 참조할 수 있다.
즉, 부모 타입의 참조 변수로 다양한 자식 객체를 다룰 수 있는 것이다.
동적 바인딩이란, 메서드 호출을 컴파일 시점이 아니라 실행 시점(런타임)에 결정하는 방식이다. 자바에서 동적 바인딩은 오버라이딩된 메서드를 사용할 때 발생하며, 참조 변수의 컴파일 시 타입과 실제 객체의 타입이 다를 경우 실제 객체의 타입에 따라 메서드가 호출된다.
이로 인해, 부모 클래스 타입의 참조 변수가 자식 클래스의 객체를 참조하고 있을 때, 자식 클래스에서 오버라이딩된 메서드가 호출된다. 자바에서 동적 바인딩은 기본적으로 적용되며, 오버라이딩된 메서드의 실행은 참조 변수의 타입이 아닌 실제 객체의 타입에 의존한다.
Parent p1 = new Child1(); // 업캐스팅: 부모 타입 참조변수로 자식 객체 참조
Child1 c1 = (Child1) p1; // 다운캐스팅: 부모 타입 참조변수를 자식 타입으로 형변환
Parent p1 = new Child1();
Parent
타입의 참조 변수 p1
이 자식 클래스 Child1
객체를 참조하고 있다.p1
의 타입은 Parent
이지만, 실제로는 Child1
객체를 참조하고 있다.Child1 c1 = (Child1) p1;
p1
을 자식 타입인 Child1
으로 명시적으로 형변환하고 있다. 이 형변환이 유효한 이유는 p1
이 실제로 Child1
객체를 참조하고 있기 때문이다.Child1
타입으로 자식 클래스의 메서드나 필드에 접근할 수 있다.c1.viewData1();
Child1
에서 오버라이딩된 메서드로, 동적 바인딩에 의해 컴파일 타임에는 Parent
타입에서 이 메서드를 호출하지만, 실행 시에는 Child1
의 viewData1()
메서드가 호출된다."Child1 viewData1() 호출"
출력.c1.viewData2();
Parent
의 메서드이다. 따라서 이 메서드는 동적 바인딩과 상관없이 부모 클래스에서 정의된 메서드가 그대로 호출된다."Parent viewData2() 호출"
출력.System.out.println(c1.data);
Child1
에서 data
필드를 선언했지만, 자바에서 필드는 오버라이딩되지 않으므로 c1.data
는 Child1
객체의 필드를 직접 참조하여 "홍길동"
이 출력된다.Parent p1 = new Child1();
: 부모 클래스 타입으로 자식 객체를 참조하는 경우, 컴파일 타임에는 p1
이 부모 클래스 타입이므로, 부모 클래스에서 메서드 호출이 결정된다.
실행 시점에 p1
이 실제로 참조하는 객체는 Child1
이므로, 오버라이딩된 viewData1()
메서드가 자식 클래스에서 정의된 대로 실행된다.
오버라이딩되지 않은 메서드(viewData2()
)는 부모 클래스의 메서드가 그대로 호출된다. 자식 클래스에서 오버라이딩하지 않았기 때문에, 부모의 메서드가 호출된다.
필드는 동적 바인딩이 적용되지 않으므로, 참조하는 객체가 어떤 클래스이든 참조 변수 타입에 정의된 필드를 직접 참조하게 된다.
- 동적 바인딩은 메서드 호출 시 참조 변수의 타입이 아니라 실제 객체의 타입에 따라 메서드를 결정하는 방식이다.
- 자식 클래스에서 부모의 메서드를 오버라이딩한 경우, 부모 클래스 타입으로 참조하더라도 자식 클래스의 메서드가 실행된다.
- 하지만 필드는 동적 바인딩이 적용되지 않으며, 참조 변수의 타입에 따라 결정된다.