[Java] 다형성과 추상클래스

션션·2026년 1월 28일

Java

목록 보기
6/10
post-thumbnail

1. 다형성(Polymorphism)

다형성이란?

상속 관계에 있을 때, 조상 클래스 타입의 참조 변수로 자식 클래스의 객체를 참조할 수 있는 성질을 말합니다. 실제 객체는 자식이지만, 겉모습(타입)은 부모의 탈을 쓰고 있는 것이죠.

JVM 메모리 관점에서의 동작

Parent p = new Child(); 코드가 실행되면 어떤 일이 벌어질까요?

  1. 스택(Stack) 영역: Parent 타입의 참조 변수 p가 생성됩니다.
  2. 힙(Heap) 영역: Child 인스턴스가 생성됩니다. 이때 메모리에는 자식 클래스의 멤버들이 모두 올라갑니다.
  3. 참조: 변수 p는 힙에 생성된 Child 객체의 주소를 가리킵니다.
  4. 접근 범위: 중요! 메모리에 자식의 멤버(major 등)가 존재하더라도, 참조 변수 p의 타입이 Parent라면 Parent에 정의된 멤버에만 접근할 수 있습니다.

동적 바인딩(Dynamic Binding)

다형성에서 가장 헷갈리는 부분이 필드와 메서드의 동작 차이입니다.

  • 필드(멤버 변수): 참조 변수의 타입을 따릅니다.
  • 메서드: 오버라이딩되었다면 참조 변수 타입과 상관없이 실제 인스턴스의 메서드(자식 클래스)가 호출됩니다. 이를 동적 바인딩이라고 합니다.
Parent p = new Child();
System.out.println(p.x);      // 부모의 필드 출력
System.out.println(p.method()); // 자식의 오버라이딩된 메서드 실행 (동적 바인딩)

참고: static 메서드는 동적 바인딩이 일어나지 않습니다. 클래스 레벨에서 관리되므로 참조 변수 타입의 영향을 받습니다. 따라서 클래스명.메서드() 형태로 호출하는 것이 권장됩니다.


2. 참조 변수의 형 변환 (Casting)

객체를 참조하는 타입을 바꿀 때 주의해야 할 규칙이 있습니다.

업캐스팅(Up-casting) vs 다운캐스팅(Down-casting)

  • 업캐스팅 (자식 → 부모): 묵시적으로 가능하며 생략할 수 있습니다. (안전함)
  • 다운캐스팅 (부모 → 자식): 명시적으로 변환해야 하며, 실제 인스턴스가 자식 타입인지 확인이 필요합니다.

instanceof 연산자와 패턴 매칭

잘못된 다운캐스팅은 ClassCastException을 발생시킵니다. 이를 방지하기 위해 instanceof로 타입을 확인해야 합니다.

  • 주의사항: if-else 문에서 사용할 때 자식 타입(좁은 범위)부터 체크해야 합니다. 부모 타입을 먼저 체크하면 자식 객체도 부모 타입으로 판정되어 로직이 꼬일 수 있습니다.
  • Java 16+ 패턴 매칭: 타입 체크와 형 변환을 한 번에 처리할 수 있습니다.
// 기존 방식
if (person instanceof Student) {
    Student st = (Student) person;
    st.study();
}

// Java 16+ 방식 (Smart Casting)
if (person instanceof Student st) {
    st.study();
}

3. 추상 클래스(Abstract Class)

왜 사용하는가?

모든 요리사(Chef)는 요리(cook)를 하지만, '그냥 요리사'가 하는 '일반적인 요리'는 정의하기 모호합니다. 이럴 때 구현부 없이 선언부만 있는 추상 메서드를 포함한 추상 클래스를 사용합니다.

특징 및 목적

  1. 객체 생성 불가: 미완성 설계도이므로 new Chef()와 같이 인스턴스를 만들 수 없습니다.
  2. 상속 전용: 하위 클래스에서 반드시 추상 메서드를 구현하도록 설계를 강제합니다.
  3. 일관성 유지: 모든 자식 클래스가 동일한 메서드 시그니처를 가지게 하여 다형성 활용을 극대화합니다.
public abstract class Chef {
    String name;
    public void eat() { System.out.println("냠냠"); }
    public abstract void cook(); // 자식에서 반드시 구현해야 함!
}

4. Tip!

super() 생성자의 중요성

자식 클래스의 생성자에서 부모의 필드를 초기화할 때, this.name = name; 보다는 super(name, age);를 호출하는 것이 좋습니다.

  • 이유: "부모의 데이터는 부모가 초기화한다"는 객체지향 원칙에 충실하며, 나중에 부모의 필드가 private으로 변경되어도 자식 코드를 수정할 필요가 없어 캡슐화에 유리합니다.

다중 상속은 왜 안 될까?

자바는 클래스의 단일 상속만 허용합니다. 만약 두 부모 클래스에 동일한 이름의 메서드가 있다면, 자식 입장에서 어떤 메서드를 상속받아야 할지 결정할 수 없는 "다이아몬드 문제"가 발생하기 때문입니다.

0개의 댓글