Brief Summary
지난 포스트에서는 선언하는 객체 타입과 할당받는 메모리의 객체 타입이 일치하는 선언-초기화 생성을 다루었다. 그러나 자바로 코딩테스트를 준비해본 사람이라면 이 모양새가 익숙할 것이다.
List<Integer> lst = new ArrayList<>();
그렇다. 자바는 선언하는 객체와 할당받는 메모리의 객체가 다른 타입인 객체 생성도 지원한다. 이것을 자바 클래스 상속 관계에 따라 업캐스팅과 다운캐스팅으로 분류하는데, 자바의 다형성을 수호하는 두 기둥이라고 할 수 있다. (물론 기둥이 2개만 있지는 않다. 다형성은 그렇게 쉬운 친구가 아니다. 유감스럽게도...)
이 포스트에서는 바인딩과 업캐스팅에 대해 설명한다. 다운캐스팅은 언젠가 느낌(!)이 오면 써 보겠다.
1. 바인딩
바인딩의 정의는 이렇다. (출처는 아마도 전공책인 것 같은데... 나중에 찾으면 달아놓겠음)
" Association of method call to the method body " (메서드 호출과 메서드 본문의 연결)
컴퓨터 프로그래밍에서 각종 값들이 확정되어 더 이상 변경할 수 없는 구속(bind) 상태가 되는 것.
바인딩에는 두 종류가 있다. 정적 바인딩과 동적 바인딩이 그것이다. 이름부터 동적은 뭔가 움직이는 느낌을 준다. 그런데 바인딩이란 게 두 값의 연결, 구속을 의미하기 때문에 이때는 아마도 값이 유동적이라는 뜻이 아닐까 추측할 수 있다. 아주 틀린 말은 아니지만 정확하게는 해당 코드가 실행된 후에 값이 확정되는 것을 말한다. 당연하게도 정적 바인딩은 그 반대로, 실행 이전에 값이 결정되는 것을 말한다.
정적 바인딩
: static, private, final method 등이 해당된다.
값이 결정되어 있기 때문에 override는 불가하다.
즉, compile 시점에 이미 타입이 결정된다.
동적 바인딩
: 대표적인 예시로 Method Overriding이 있다.
슈퍼(부모)클래스와 서브(자식)클래스에 모두 동일한 메서드가 존재할 경우, 둘 중 어느 메서드가 실행될 지는 코드 실행 전 compile 시점에서는 결정되지 않는다. 즉, 코드 실행(Run time) 시점에 타입이 결정되어 해당 타입의 Method가 실행되는 방식이다.
2. 업캐스팅
동적 바인딩을 적용한 대표적인 작업이 업캐스팅에서의 메서드 활용이다. 업캐스팅이란, 서브 클래스의 객체에 대한 레퍼런스를 슈퍼 클래스 타입으로 변환하는 작업을 의미한다. 서브 클래스 객체로 슈퍼 클래스의 멤버를 활용하기 위해 사용한다.
ps. 클래스의 멤버 = 필드 + 메서드 + 생성자
서브 클래스의 객체 레퍼런스를 슈퍼 클래스 타입으로 변환한다는 것은 슈퍼 클래스의 레퍼런스가 서브 클래스의 객체를 가리키도록 한다는 것을 말한다. 그렇게 되면 해당 레퍼런스로는 슈퍼 클래스의 멤버를 통해서만 접근할 수 있게 된다. 하지만 예외적으로 자식 클래스에서 오버라이드(재정의)된 슈퍼 클래스의 메서드는 자식 클래스의 것으로 사용한다. 이때 동적 바인딩이 발생한다.
3. B 타입에서 A 클래스로의 업캐스팅과 오버라이딩
이쯤되면 예시가 간절해진다. 긴 설명은 잠시 치우고, 다음 코드를 보자. 여기서 B는 A라는 슈퍼 클래스를 상속받은 서브 클래스이다.
A a = new B();
A 객체 타입을 가리키는 레퍼런스 변수에, B 타입 크기의 메모리를 할당하고 B()
생성자를 실행하고 있다.
그런데 이 코드는 곧 다음 과정을 압축한 것이기도 하다.
A a = new A();
B b = new B();
a = b; // 일종의 자동 박싱?
즉, B 타입 크기의 메모리와 해당 생성자로 초기화된 B 타입 객체를 A 타입의 레퍼런스로 가리키게 한 것이다.
정리하자면, 서브 클래스(B
)의 객체에 대한 레퍼런스(b
)를 슈퍼 클래스 타입(A
)으로 변환(b
→a
)하는 작업이다. 슈퍼 클래스(A
)의 레퍼런스(a
)가 서브 클래스(B
)의 객체((b)
)를 가리키도록 했기 때문에, 이 레퍼런스(a
)로는 슈퍼 클래스(A
)의 멤버만 접근 가능하다.
그런데, 원래 a는 A 클래스의 메서드만 사용한다. 그러나, B는 A의 서브 클래스로서 A의 어떤 메서드를 필요에 의해 재정의(오버라이드) 했을 것이다. 그리고 지금, a는 B 클래스의 객체 b를 가리키고 있다. 때문에 B에서 A의 것을 오버라이드한 메서드를 가지고 있다면 a가 해당 메서드를 실행할 때, a의 의사와는 상관없이(?) B의 메서드가 실행된다.
정리
: 종합하면 이렇게도 말할 수 있겠다.
A a = new B();
에 대해,
- A 타입으로 선언된 객체 a가 B 클래스의 특정 메서드를 사용할 수 있는 이유는?
B 클래스의 객체에 오버라이딩 된 메서드가 존재하기 때문이다. 그 외의 메서드는 사용할 수 없다. (a가 사용할 수 있는 메서드 = A클래스 메서드 + B 클래스에서 재정의한 메서드) - 반대로, B 클래스의 객체가 A 클래스로 업캐스팅 당한 상황에서 B 클래스 메서드의 호출을 보장하는 방법은?
B 클래스에서 A 클래스의 해당 메서드를 오버라이딩하면 된다.
참고:
<명품 JAVA Programming> 황기태, 김효수 / 생능출판
티스토리 - 자바비터
'CS 이론 정리' 카테고리의 다른 글
객체 생성의 모든 것 (1) - 문자열 (1) | 2024.11.08 |
---|---|
Static의 모든 것 (1) | 2024.11.07 |