일단 씻고 나가자
업 캐스팅 / 다운 캐스팅 (UpCasting / DownCasting) 본문
계속 이해를 고민했던 부분인데 비로소 명확히 이해가 되어 정리겸 포스팅한다.
최대한 직관적인 이해 및 코드로 설명한다.
헷갈리는 부분만 보고 싶은 사람, 혹은 정리만 필요한 사람은
포스트의 가장 하단 부분으로 이동하자.
직관적인 설명
먼저, Java는 명확한 언어이기 때문에 어물쩍 넘어가는 걸 싫어한다.
따라서 배열이든 컬렉션(List 등)이든, 꼭 한 종류만 담아야 한다.
이를 머릿속에 넣고 시작한다.
예시를 보자.
당신은 이사를 위해 이삿짐을 트럭에 담고 있다.
이삿짐은 음식, 가구, 전자기기등 다양하다.
트럭 = 배열 or 컬렉션 / 이삿짐 = 객체
Java 라는 친구는 하나의 트럭에 하나의 종류밖에 못 담게 한다고 했다.
만약 종류가 다른 세 물건을 하나의 트럭에 담는다면 Java 는 불같이 화를 낼 것이다.
그럼 방법은 뭘까?
뭐긴, 트럭을 종류별로 세 대 준비하는 것이다.
트럭 하나 부르는 것도 비싼데..
거기다가 첫 번째 트럭은 딸랑 사과 하나만 담아서 공간 낭비가 너무 심하다.
객체의 종류별로 배열 or 컬렉션을 선언하는 것은 공간 및 자원의 낭비가 심함
또한 서로 다른 종류의 객체를 묶어 활용할 수 없게 됨
그래서 Java에게 살짝 귓속말을 했다.
저것들은
각자 다른 종류가 아니라
' 물건 ' 이라는 하나의 종류야 ^^
UpCasting
Java 가 살펴 보니,
' 물건 ' 이라는 종류가 가지는 ' 색, 모양, 촉감 .. ' 등의 성질을 셋 다 모두 가지고 있었다.
Java 는 꺼림직했지만 딱히 반박할 말이 없어 허용해주었다.
각 객체의 공통된 부모 클래스로 UpCasting 하여, 부모 클래스로 선언된 배열 or 컬렉션에 담을 수 있음
단, 담으려는 모든 객체의 부모 클래스가 같아야 함
얏호! 덕분에 성공적으로 하나의 트럭에 모두 담을 수 있게 됐다!
하지만 Java 의 감시가 너무 심해서,
트럭에 타 있는 동안엔 사과를 먹을 수도, 침대에 누울 수도, 노트북을 이용할 수도 없다.
그저 ' 저 물건은 빨간색이네.. ' 정도를 얘기할 수 있을 뿐..
배열 혹은 컬렉션에 들어가 있는 캐스팅된 객체는
각 객체가 혼자만 가지고 있는 고유의 메서드 혹은 필드는 활용할 수 없고,
모든 객체가 공통되게 가지고 있는 메서드 혹은 필드만을 활용할 수 있음
근데 문제는,
이사를 마치고,
침대를 꺼내서 침대에 눕고 싶은데,
Java 의 감시가 끝나지 않았다는 것이다.
' 물 건 ' 이니까
' 색, 모양, 촉감 .. ' 만 사용할 거지? ^^
눕는다는 얘긴 없었잖아? ^^
어차피 이사는 끝났다.
Java 에게 난 당당히 얘기한다.
저건
' 물건 ' 이 맞지만,
' (침대) 물건 ' 이라고 읽는단다 ^^
DownCasting
Java 는 이번에도 할 말이 없었다.
불쌍한 Java 는 다음에도, 또 다음에도, 또또 다음에도 이런 과정을 반복할 것이다.......
배열 or 컬렉션에 담긴 객체를 꺼내어 고유의 성질을 활용할 땐
DownCasting을 통해 본래의 객체로 되돌려 놓아야 활용할 수 있게 됨
코드 설명
필요에 의해 여러 종류의 class 및 객체가 생성될 것이지만,
필연적으로 포괄적인 넓은 범위로 객체를 활용해야 할 상황이 존재한다.
가장 흔한 경우가 앞선 예시의 하나의 배열 or 컬렉션에 상이한 종류의 객체를 저장하는 것이다.
그림과 같이 생성 당시 선언된 종류를 기준으로 다른 종류는 담을 수 없다.
이를 해결하기 위해
공통점을 추출한 부모 클래스를 두어 상속의 성질로 하나의 종류로 속일 수 있다.
이렇게 상위 클래스로 바꾸는 것을 UpCasting 이라 한다.
다만 위의 예시에서, 물건의 '색' 변수는 구현되는 물건에 따라 달라지므로
클래스마다 적절한 오버라이딩을 필요로 한다.
이렇게 담긴 개별 객체는 정상적인 역할을 할 수 있을까?
아니다. 앞선 예시에서 보았듯,
이들은 '상위 클래스에서 선언된 역할' 만을 할 수 있다.
'색' 의 경우 '사과' 클래스에서 따로 정의하지 않았지만 상속 받은 부모의 변수가 자동 내재 된다.
(이를 바꾸기 위해선 오버라이딩이 따로 필요하다)
눈여겨야 할 점은 '사과' 클래스에서 선언한 '음식' 변수를 활용하지 못하는 점이다.
분명 사과 객체를 담아 넣었지만, 아직은 '물건' 의 취급을 받고 있기 때문에
'물건' 클래스에서 정의된 부분만 활용가능하다.
필요에 의해 배열에 담긴 했지만,
'물건' 이 아닌 '사과' 고유의 성질을 활용해야 할 때가 분명 있을 것이다.
이를 DownCasting 을 통해 고유의 '사과' 로 활용할 수 있다.
DownCasting 은 명확히 원래의 객체로 돌아가는 의미인만큼
원래 객체의 클래스를 소괄호를 통해 명시해주어야 한다.
이를 '명시적 형변환' 이라 칭한다.
즉, UpCasting 한 객체를 원 객체로 되돌리는 것이 DownCasting 이며,
UpCasting 이 없다면, 혹은 UpCasting 이 되지 않은 객체라면 DownCasting 이 불가능하다.
( DownCasting 만이 가능은 하다. (에러는 나지 않는다)
하지만 다운캐스팅 만 한다고 캐스팅 된 클래스의 속성을 쓸 순 없다. 즉, 의미 없음 )
정리
여태까지의 내용과 헷갈리는 부분을 정리하면 다음과 같다.
- 부모 parents = new 자식(); // UpCasting,
자식 child = (자식) parents // DownCasting. - UpCasting 이 선행되어야 DownCasting 도 있는 것이다.
DownCasting 만 할 순 없음. - '포함 관계' 와 '사용할 수 있는 범위' 는 반비례 관계이다.
예를 들어, 부모 클래스는 자식 클래스를 포함하지만, 부모 클래스에서 활용할 수 있는 메서드나 변수는 더 적다. - UpCasting 의 의의는 '공통된 부분의 사용' 에 있다.
예를 들어, 모든 물건의 '색' 만 알고 싶은 경우,
사과 따로 조사.. 침대 따로 조사.. 할 필요 없이 '물건'으로 한 데 묶어 for문을 돌면 색만 추출할 수 있다. - DownCasting 의 의의는 '객체 개별 특성의 사용' 에 있다.
예를 들어, '물건' 으로 UpCasting 된 '사과' 객체는 더이상 '맛, 영양소..' 등 고유의 성질에 접근할 수 없다.
이때 해당 객체를 DownCasting 하면 다시 오롯한 '사과' 객체가 되어 고유 성질을 활용할 수 있다. - '명시적 형변환' 은 DownCasting 에서만 해주면 된다.
이렇게 이해하자.
UpCasting 은 내 고유의 성질을 잃는 것이니 대충 끼워맞추면 되지만,
DownCasting 은 내 거 되돌려달라고 강력하게 말해야 하니까 형변환이 필요하다.
실제 Java 에서는 모든 클래스의 부모 클래스인 Object 가 존재하여
다른 클래스이든, 혹은 임의로 내가 만든 클래스라도 Object 클래스로 UpCasting 하여 활용할 수 있다.
개념적인 부분의 이해만으론 원활한 사용이 불가능하니,
직접 활용하여 익숙해지는 것이 좋겠다.