9osari.log
← All posts

[JAVA] 다형성_1

April 26, 2025 · 8 min read · #java#김영한-java-기본

다형성이란?

보통 하나의 객체는 하나의 타입으로만 사용된다.

하지만 다형성을 사용하면 하나의 객체가 여러 타입으로 사용될 수 있다.

다형성을 이해하려면 두 가지 개념을 알아야 한다:

  1. 다형적 참조 (Polymorphic Reference)
  2. 메서드 오버라이딩 (Method Overriding)

다형적 참조 (Polymorphic Reference)

다형성을 이해하려면 먼저 다형적 참조 개념을 알아야 한다. 다형적 참조는 부모 타입의 변수로 자식 인스턴스를 참조하는 것 을 의미한다.

// Parent.java
package poly.basic;

public class Parent {
    public void parentMethod() {
        System.out.println("Parent.parentMethod");
    }
}
// Child.java
package poly.basic;

public class Child extends Parent {
    public void childMethod() {
        System.out.println("Child.childMethod");
    }
}
// PolyMain.java
package poly.basic;

public class PolyMain {
    public static void main(String[] args) {
        System.out.println("Parent -> Parent");
        Parent parent = new Parent();
        parent.parentMethod();

        System.out.println("Child -> Child");
        Child child = new Child();
        child.parentMethod();   // 상속받은 메서드 호출 가능
        child.childMethod();    // 자식 고유 메서드 호출 가능

        System.out.println("Parent -> Child");
        Parent poly = new Child();
        poly.parentMethod();    // 부모 타입에서 선언된 메서드만 호출 가능

        // poly.childMethod(); // 컴파일 오류 - Parent 타입에는 존재하지 않는 메서드
    }
}

// 실행결
Parent -> Parent
Parent.parentMethod
Child -> Child
Parent.parentMethod
Child.childMethod
Parent -> Child
Parent.parentMethod

부모 타입 변수 → 부모 타입 인스턴스

Parent parent = new Parent();

image.png

자식 타입 변수 → 자식 타입 인스턴스

Child child = new Child();

![image](/assets/img/poly/1/image 1.png)

부모 타입 변수 → 자식 타입 인스턴스 (다형적 참조)

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

![image](/assets/img/poly/1/image 2.png)

poly.childMethod(); // 컴파일 오류 발생

캐스팅

Child casted = (Child) poly; // 형변환 캐스팅
casted.childMethod(); // 가능

형변환을 사용하면 자식 타입에만 있는 기능도 사용할 수 있다.

이처럼 부모 타입의 참조 변수를 자식 타입으로 변환하는 것을 다운캐스팅(downcasting) 이라고 한다.

![image](/assets/img/poly/1/image 3.png)

Child child = (Child) poly; 이 코드는 poly가 가리키는 인스턴스(실제 객체)가 Child 타입이라는 것을 가정하고, 그 참조값을 Child 타입 변수에 대입하는 것이다.

참고: poly의 참조값만 복사되는 것이고,

poly 자체의 타입은 여전히 Parent이다. 변하는 것은 아니다.

일시적 다운캐스팅

다운캐스팅 결과를 변수에 담지 않고, 필요한 순간에만 일시적으로 타입을 변환해서 메서드를 호출할 수도 있다.

((Child) poly).childMethod();

![image](/assets/img/poly/1/image 4.png)

이 캐스팅은 단지 poly가 가리키는 참조값을 Child로 해석해서 호출하는 것 뿐이다. 실제로 poly의 타입 자체가 바뀌는 것은 아니다. 즉, poly는 여전히 Parent 타입 이다.

업캐스팅

다운캐스팅과 반대로, 자식 타입을 부모 타입으로 변환하는 것을 업캐스팅 이라고 한다.

Child child = new Child();
Parent parent1 = (Parent) child; // 명시적 업캐스팅 (사실 생략 가능)
Parent parent2 = child;          // 암묵적 업캐스팅 (추천 방식)

// 실행 결과
Parent.parentMethod
Parent.parentMethod
Parent parent = new Child(); // 업캐스팅 생략한 형태

업캐스팅은 자바 컴파일러가 자동으로 처리해준다. 반면, 다운캐스팅은 명시적인 형변환이 없으면 컴파일 오류가 발생 한다.

다운캐스팅과 주의점

다운캐스팅은 실제 인스턴스가 자식 타입일 때만 안전 하게 사용할 수 있다. 잘못된 다운캐스팅은 ClassCastException이라는 런타임 오류를 발생시킨다.

Parent parent1 = new Child();
Child child1 = (Child) parent1;
child1.childMethod(); // OK

Parent parent2 = new Parent();
Child child2 = (Child) parent2; // 런타임 오류 발생
child2.childMethod(); // 실행되지 않음

왜 오류가 생길까

Parent parent2 = new Parent(); // 실제 인스턴스는 Parent
Child child2 = (Child) parent2; // 존재하지 않는 기능에 접근하려고 함
Child.childMethod
Exception in thread "main" java.lang.ClassCastException: class poly.basic.Parent cannot be cast to class poly.basic.Child
✅

다운캐스팅은 부모가 자식을 알고 있을 때만 가능하다 즉, 실제 인스턴스가 자식이어야 한다는 뜻이다.

안전하게 캐스팅하려면?

자바에서는 다운캐스팅 전에 instanceof 키워드를 사용해서 실제 인스턴스 타입을 검사할 수 있다.

if (parent2 instanceof Child) {
    Child safeChild = (Child) parent2;
    safeChild.childMethod();
} else {
    System.out.println("Child 타입이 아닙니다.");
}

→ 이렇게 검사하면 런타임 오류 없이 안전하게 다운캐스팅할 수 있다.

업캐스팅은 안전하고, 다운캐스팅은 왜 위험할까?

업캐스팅은 항상 안전하다

A a = new C(); // 업캐스팅

그래서 업캐스팅은 형변환 생략도 가능하고, 자바에서 매우 자주 쓰인다

다운캐스팅은 조심해야 한다

C c = (C) new B(); // 런타임 오류 (ClassCastException)

하위 타입은 객체 생성 시 포함되지 않기 때문에, 존재하지 않는 부분을 참조하게 될 위험이 있다.

컴파일 오류 vs 런타임 오류

구분설명예시확인 시점
컴파일 오류문법적 오류, 즉시 확인 가능변수 오타, 클래스 없음코드 작성 시
런타임 오류실행 중 발생하는 오류잘못된 다운캐스팅프로그램 실행 중

instanceof

다형성에서는 부모 타입 변수가 다양한 자식 인스턴스를 참조할 수 있기 때문에,

현재 참조하고 있는 실제 인스턴스의 타입을 확인할 필요가 있다.

이때 사용하는 것이 바로 instanceof 연산자이다.

Parent parent1 = new Parent();
Parent parent2 = new Child();

이처럼 부모 타입 변수는 다양한 자식 인스턴스를 참조할 수 있으므로,

다운캐스팅 전에 instanceof로 검사하는 것이 안전한 코드 작성에 도움이 된다.

instanceof 결과 예시

private static void call(Parent parent) {
    parent.parentMethod();

    if (parent instanceof Child) {
        System.out.println("Child 인스턴스 맞음");
        Child child = (Child) parent; // 안전한 다운캐스팅
        child.childMethod();
    }
}

new Parent() instanceof Parent  // true
new Child() instanceof Parent   // true (부모 타입도 포함됨)
new Parent() instanceof Child   // false
new Child() instanceof Child    // true

Java 16부터: 패턴 매칭 기반 instanceof

Java 16부터는 instanceof를 사용할 때 타입 검사 + 캐스팅 + 변수 선언을 한 번에 처리할 수 있다.

if (parent instanceof Child child) {
    child.childMethod();
}

정리

다형성과 메서드 오버라이딩

다형성을 이루는 또 하나의 핵심 이론은 바로 메서드 오버라이딩(Overriding) 이다. 오버라이딩은 부모 클래스에 정의된 메서드를 자식 클래스에서 재정의하는 것이다.이때 중요한 점은, 오버라이딩 된 메서드는 항상 우선권을 가진다.

다형성과 함께 사용할 때, 메서드 오버라이딩의 진짜 힘이 발휘된다.

// Parent.java
public class Parent {
    public String value = "parent";
    public void method() {
        System.out.println("Parent.method");
    }
}

// Child.java
public class Child extends Parent {
    public String value = "child";

    @Override
    public void method() {
        System.out.println("Child.method");
    }
}
public class OverridingMain {
    public static void main(String[] args) {
        Child child = new Child();
        System.out.println("Child -> Child");
        System.out.println("value = " + child.value);
        child.method();

        Parent parent = new Parent();
        System.out.println("Parent -> Parent");
        System.out.println("value = " + parent.value);
        parent.method();

        Parent poly = new Child();
        System.out.println("Parent -> Child");
        System.out.println("value = " + poly.value); // 변수는 오버라이딩되지 않음
        poly.method(); // 메서드는 오버라이딩됨
    }
}

//실행결과
Child -> Child
value = child
Child.method
Parent -> Parent
value = parent
Parent.method
Parent -> Child
value = parent
Child.method

오버라이딩의 우선순위

오버라이딩된 메서드는 항상 하위 클래스 쪽 메서드가 우선순위를 가진다.

예를 들어,

정리

이 조합이 바로 다형성의 진짜 위력이며, 유연하고 확장 가능한 객체지향 설계의 핵심이다.


출처: [김영한의 실전 자바 - 기본편](https://www.inflearn.com/course/김영한의-실전-자바-기본편/dashboard)
···
← PREV [JAVA Questions] 생성자 예상질문 NEXT → [JAVA] 다형성_2