자바Java 캐스팅의 원리 — 업캐스팅과 다운캐스팅 제대로 이해하기

scminy0527·2026년 2월 23일
post-thumbnail

자바를 배우다 보면 반드시 마주치는 개념이 있다.

바로 업캐스팅(Upcasting) 과 다운캐스팅(Downcasting).

처음에는 이렇게 비유하고 싶어질 수 있다.

부모: 500ml 병, 자식: 1.5리터 병 (부모는 자식에게 아낌없이 주기에 자식이 더 큼)
1.5리터 음료를 500ml 병에 담는 것 = 업캐스팅
500ml를 1.5리터 병에 담는 것 = 다운캐스팅

그런데…
이 비유는 생각 하면 500ml를 1.5리터 병에 담는건 문제 없고 (다운캐스팅)
1.5리터 음료를 500ml 병에 담는게 (업캐스팅) 오히려 문제가 있을거라고 생각한다.

그런데 이건 잘못된 생각이다.

오늘은 캐스팅의 진짜 원리를
메모리 개념을 아주 쉽게 풀어서 정리해보려고 한다.


1. 기본 구조부터 이해하기

class Parent {
    void method() {
        System.out.println("부모 메서드");
    }
}

class Child extends Parent {
    @Override
    void method() {
        System.out.println("자식 메서드");
    }

    void childMethod() {
        System.out.println("자식 전용 메서드");
    }
}

2. 업캐스팅이란?

Child c = new Child();
Parent p = c;   // 업캐스팅

또는

Parent p = new Child();  // 업캐스팅

의미

자식 객체를 부모 타입으로 다루는 것.


3. 여기서 많이 하는 오해

업캐스팅을 하면 500ml(부모), 1.5리터(자식) 페트병을 떠올리면서

"부모영역이 넘쳐서 오류 나는거 아닌가?"

라고 생각하기 쉽다.

하지만 ❌ 그렇지 않다.

JAVA 의 출시 시대 이해

  • 자바는 1991년 경에 출시
  • 그 당시 외장 메모리 용량은 2메가 정도

따라서 메모리 낭비는 용납 못하던 시기

즉 메모리가 낭비가 되는 코드는 존재할 수 없다.
일단 부모 타입으로 업캐스팅을 하면 넘치더라도 남는 부분이 없고, 자식 영역으로 다운캐스팅을 하면 빈 공간이 너무 많이 남는 메모리 낭비가 발생 하기에 부적절

이런식으로 이해를 하면 더 쉽게 캐스팅을 구분할 수 있다.

업캐스팅 시 발생하는 일

업 캐스팅 시에는 상속을 받은 자식 클래스 (부모 타입 + 자식 타입) 에서 자식 영역을 제외한 부모 영역의 코드만 접근을 할 수 있는 상태가 됨.

따라서 상속을 해서 만든 자식 클래스에 있는 자식 매서드는 실행이 되지 않는다.


4. 그럼 오버라이딩은 왜 동작할까?

Parent p = new Child();
p.method();

이 코드를 실행하면 무엇이 출력될까?

자식 메서드

이유는 자바가 실제 객체 타입을 기준으로 메서드를 실행하기 때문이다.

즉,

  • 참조 타입은 Parent
  • 실제 객체는 Child

👉 실행은 Child 기준으로 이루어진다.

이걸 다형성(Polymorphism) 이라고 한다.


더 쉽게 이해를 하면

상속에서 매서드 오버라이딩 시 매서드를 다음과 같이 느끼면 된다.

매서드 = 코드를 담는 저장공간(변수)

즉 다음과 같은 느낌이 된다.

  • 부모 영역: method 라는 변수에 매서드 코드를 정의
  • 자식 영역: method 라는 변수에 자식영역의 코드를 덮어씌움

마치 int num = 1; 을 한 뒤에
num = 3; 을 하는거랑 비슷

그리고 부모 영역에서의 코드만 보게 되면 오버라이딩 한 매서드는 태싱이 부모 코드
따라서 아무리 자식 영역에서 오버라이딩을 했다고 하더라도 업캐스팅을 해서 부모 영역 단위에서 보더라도 매서드는 무리 없이 수행이 된다.


5. 다운캐스팅은 왜 위험할까?

Parent p = new Parent();
Child c = (Child) p; // 오류 발생

이건 왜 안 될까?

Parent 객체 안에는
Child 고유의 기능이 존재하지 않기 때문이다.

즉, 존재하지 않는 영역을 강제로 쓰려는 것이기 때문에
런타임 에러(ClassCastException)가 발생한다.


6. 다운캐스팅이 가능한 경우

Parent p = new Child();   // 업캐스팅
Child c = (Child) p;      // 다운캐스팅 가능

이건 왜 가능할까?

처음부터 실제 객체는 Child 전체 객체였기 때문이다.

업캐스팅은 숨기는 것일 뿐
없애는 것이 아니다.

그래서 다시 자식 타입으로 되돌릴 수 있다.


7. 캐스팅을 왜 사용하는가?

이게 핵심이다.

void process(Parent p) {
    p.method();
}

이렇게 만들면

  • Child
  • 또 다른 자식 클래스
  • 여러 파생 클래스

모두 받을 수 있다.

이게 바로 다형성의 힘이다.

하나의 메서드로 여러 객체를 처리할 수 있다.


8. 안전한 다운캐스팅

if (p instanceof Child) {
    Child c = (Child) p;
}

instanceof는

👉 "이 객체가 정말 자식 타입이 맞는가?"
를 확인하는 안전장치이다.


정리

구분의미
업캐스팅자식 객체를 부모 타입으로 다루기
다운캐스팅부모 타입을 자식 타입으로 변환
객체 크기new 할 때 이미 결정됨
오버라이딩실제 객체 기준으로 실행
목적다형성 구현

개인적인 깨달음

처음에는 캐스팅을
"메모리 크기 문제"로 이해하려고 하면 어려울 수 있다.

하지만 대신에

타입을 어떻게 바라볼 것인가의 문제

로 보면 더 이해하기 쉬울수도 있을거같다.

업캐스팅은

👉 객체를 부모 시점에서 바라보는 것

다운캐스팅은
👉 다시 자식 시점으로 바라보는 것

profile
비전공자에서 시작해 Flutter로 실서비스를 만들었고, 더 깊은 개발을 향해 계속 성장 중인 개발자

0개의 댓글