업캐스팅(upcasting)과 다운캐스팅(downcasting)을 이해해보자!
요즘 시험기간이라 업캐스팅, 다운캐스팅을 공부하고 있는데 이해가 도저히 안 되는 거에요,,
그래서 제가 이해하고자, 또 다른 사람들에게 더 쉽게 알려주고자 업캐스팅과 다운캐스팅에 관련하여 글을 쓰려 합니다!
업캐스팅과 다운캐스팅 모두에 들어있는 바로 이 Casting은 무엇일까요?
캐스팅은 데이터 타입이 다른 경우에 하나의 데이터 타입으로 통일해주기 위해 사용합니다.
정수와 실수를 더하려면 두 변수의 데이터 타입이 같아야 하는 경우에 사용이 되겠죠?
캐스팅에는 총 두 가지의 종류가 있습니다.
프로그래머의 명시적인 요청 없이 컴파일러가 자동으로 타입을 변환하는 것
자동형변환이 일어나는 경우에는 작은 범위의 데이터타입에서 큰 범위의 데이터 타입으로 변환하는 경우에 발생하게 됩니다.
int i = 10;
double d = 3.14;
double result = d + i; // i가 자동으로 실수형으로 변환됨
위와 같이 int
타입을 double
타입으로 변환할 때 자동적으로 사용되겠죠.
프로그래머가 직접 타입을 변환하는 것
명시적형변환은 큰 범위의 데이터타입을 작은 범위의 데이터타입으로 강제적으로 형변환할 때 사용하게 됩니다.
자신보다 작은 곳에 자신을 맞춰야 하므로 손실이 생길 수 있으며, 꼭 ()
를 사용하여 강제형변환을 할 타입을 적어주어야 합니다.
int i = 10;
double d = (double)i;
이후 우리가 알아볼 업캐스팅은 자동형변환에 해당하고, 다운캐스팅은 명시적형변환에 해당합니다.
그렇다면 업캐스팅은 과연 무엇일까요?
업캐스팅(upcasting)이란? 서브 클래스 객체를 슈퍼 클래스 타입으로 타입 변환하는 것
서브 클래스 객체를 슈퍼 클래스 타입으로 타입 변환을 한다는 것에서 우리는 업캐스팅과 다운캐스팅은 상속 관계에서만 이루어진다는 것을 유추할 수 있겠죠?
솔직히 이론으로는 잘 와닿지 않을 것입니다. 바로 예제를 볼까요?
부모클래스인 Person
과 Person
클래스를 상속받은 자식클래스인 Student
클래스가 있습니다.
main
에서 Person p
가 생성이 되었죠? 이 때는 객체가 생성되지 않고 그저 p
라는 이름의 껍데기만 만들어진 상태입니다.
바로 다음에 자식클래스인 Student s = new Student("이재문");
의 형태로 객체가 생성이 되었습니다.
이 때, Student
는 Person
클래스를 상속받았기 때문에
위의 모든 멤버변수와 메서드를 바라보며 접근할 수 있습니다.
그 다음을 볼까요? 드디어 업캐스팅이 이루어졌습니다.
Person p;
Student s = new Student("이재문");
p = s;
위의 코드는 아래와 같습니다.
Person p;
Student s;
p = new Student("이재문");
s
가 업캐스팅이 되었습니다. 우리는 이것을 s
를 p
로 부른다. 라고 칭하겠습니다.
자식클래스를 슈퍼클래스로 부른다. 라고도 말할 수 있겠군요.
자동차와 전기차가 그 예시입니다.
자동차를 부모클래스라고, 전기차를 자식클래스라고 칭해봅시다.
우리는 전기차를 볼 때 "전기차다!" 라고 할 수도 있지만 "자동차다!"라고 말할 수도 있습니다.
하지만 아무 자동차나 보고서 "전기차다!" 라고 말을 할 수는 없겠죠? 자동차 중에서는 전기차도 있지만 전기차가 아닌 차도 많으니까요.
따라서 p = s
를 통해 우리는 전기차를 자동차라고 부를 수 있는 것입니다.
이렇게 전기차를 자동차로 업캐스팅시켰을 때, 볼 수 있는, 즉 접근할 수 있는 멤버변수와 메서드는 자동차인 Person
클래스 안의 멤버와 메서드밖에 없습니다.
우리는 전기차가 아닌 자동차의 시선으로 바라보고 있기 때문에 Student
클래스가 바라보는 것이 아닌 Person
클래스가 바라보는 것만을 접근할 수 있는 것입니다.
밑의 컴파일 오류가 나는 부분을 볼까요?
p
객체는 지금 s
객체입니다. 하지만 Person
타입으로 업캐스팅이 된 상태이므로 사실상 Person
클래스가 접근할 수 있는 것들인 name
id
Person(String name)
만 사용할 수 있습니다.
그래서 Person
클래스 안에는 name
멤버변수는 있지만 grade
와 department
는 p
가 볼 수 없는 Student
클래스에 있기 때문에 접근하지 못해 오류가 나는 것입니다.
- 업캐스팅은 자동형변환(형변환)에 해당한다.
- 업캐스팅과 다운캐스팅은 상속관계에서만 이루어질 수 있다.
s
를Person
타입으로 업캐스팅시키면s
객체는Person
이 바라보는 것들만을 접근할 수 있다.
위의 업캐스팅을 이해했다면 다운캐스팅은 훨씬 쉽습니다.
슈퍼클래스 객체를 서브클래스 타입으로 변환하는 것
쉽게 말해 업캐스팅으로 인해 서브클래스 객체가 슈퍼클래스 타입이 된 것을 원래대로 되돌려 놓는다는 얘기입니다.
좁아진 시선을 다시 원래대로 넓힌다고 말할 수도 있겠네요.
앞에서 다운캐스팅은 명시적형변환이라고 말했던 기억이 나시나요?
명시적형변환을 할 때에는 타입 변환 표시를 꼭 해주어야 했듯, 다운캐스팅을 할 때에도 자식클래스의 타입을 반드시 명시해주어야 합니다.
class Person {..}
class Student extends Person {...}
...
Person p = new Student("이재문"); // 업캐스팅
...
Student s = (Student)p; // 다운캐스팅
더 정확한 이해를 위해 다운캐스팅 예시를 함께 봅시다.
위의 사진에서
Person p = new Student("이재문");
으로 업캐스팅이 되었죠? 그렇다면 p
는 Student
객체지만 Person
클래스가 볼 수 있는 name
id
Person()
만 접근하여 사용할 수 있을 것입니다.
그 다음 새로운 Student
객체인 s
가 선언이 되고,
그 이후에 Student
객체이며 Person
타입인 p
를 (Student)p
로 인해 다시 Student
타입으로 변환하는 다운캐스팅이 일어납니다.
이러한 다운캐스팅을 통해 다시 p
는 Student
타입이 되며 grade
department
Student()
도 함께 접근하여 사용할 수 있게 됩니다.
Student타입인 것과 Student객체인 것은 엄연히 구분되어야 합니다. "타입"과 "객체"에 주의하며 다시 정독해보세요!
주의할 점입니다! 다운캐스팅은 반드시 업캐스팅이 된 객체에만 가능합니다.
업캐스팅이 되지 않은 부모객체에 다운캐스팅을 시도하는 것은 생각해보면 당연히 안 되는 것입니다.
원래 상속관계에서는 부모클래스가 자식클래스에 접근할 수 없는데 부모클래스를 다운캐스팅하는 행위는 이가 가능하도록 만들어줄 수 있기 때문이죠.
단순히 업(UP)을 해주었으니 다시 다운(DOWN)을 해주는 것이라고 생각하면 더 이해가 쉬우실 겁니다.
💡( 참고 ) 업캐스팅이 되지 않은 객체에 다운캐스팅을 시도하면 각각의 실행과 컴파일이 될까요??
정답은 컴파일은 되지만 실행은 되지 않습니다.
그럼 다시 예제 사진으로 넘어가서 오류가 나는지 안 나는지를 볼까요?
s.name
을 출력하면 이재문
이라는 이름이 잘 출력이 될 것입니다.
이것은 p
를 다운캐스팅하지 않아도 접근이 가능했던 부분입니다.
s.grade = "A";
부분 또한 오류가 나지 않습니다.
이는 p
를 다운캐스팅한 것을 s
에 저장하여 Student
타입이 되었기 때문에 grade
에 접근이 가능해진 덕분입니다.
- 다운캐스팅은 명시적형변환에 해당하여 형변환하는 자식클래스의 타입을 명시해주어야 한다.
- "타입"인 것과 "객체"인 것은 다르다. 부모클래스 타입인데 자식클래스 객체이면 업캐스팅이 된 것이라고 한다.
- 다운캐스팅은 업캐스팅이 된 객체에만 가능하다. 이는 실행때 오류가 나게 된다.
업캐스팅과 다운캐스팅을 이해하는 데에 도움이 되었길 바랍니다!!