Topic (오늘의 주제)
객체지향의 꽃, 다형성 (Polymorphism)
하나의 객체(또는 메소드)가 여러 가지 타입을 가질 수 있는 성질을 의미하며, 주로 자바와 같은 언어에서는 "부모 타입의 참조 변수로 자식 타입의 인스턴스를 참조하는 것"으로 구현됩니다.
Why (왜 사용하는가? 왜 중요한가?)
이 개념이 실무, 설계, 면접에서 중요한 이유
- 실무: 이 개념이 없으면 새로운 기능(예: 새로운 결제 수단)을 추가할 때마다 기존 코드를 일일이 수정(if-else 지옥)해야 하므로 유지보수가 불가능에 가까워집니다.
- 구조적 의미: **OCP (개방-폐쇄 원칙)**를 가능하게 하는 핵심 도구로, 클라이언트 코드가 구체적인 클래스가 아닌 **추상화(인터페이스)**에 의존하게 하여 결합도(Coupling)를 낮춥니다.
- 면접 의도: 지원자가 단순히 코드 작성을 넘어, "유연하고 확장 가능한 설계를 할 수 있는가?"를 판단하는 가장 중요한 척도입니다.
Core Concept (핵심 개념 정리)
| 요소 | 내용 |
| 개념 정의 | "하나의 참조 변수로 여러 형태의 객체를 참조할 수 있는 능력." (One Interface, Multiple Implementations) |
| 동작 방식 | 1. 상속/구현: 부모 클래스나 인터페이스를 상속받음. 2. 오버라이딩: 자식 클래스에서 메소드를 재정의함. 3. 업캐스팅: 부모 타입 변수에 자식 인스턴스를 대입함. 4. 동적 바인딩: 실행 시점(Runtime)에 실제 인스턴스의 메소드가 호출됨. |
| 장점 | 유연성 및 확장성: 기존 코드를 건드리지 않고 새로운 클래스를 추가하여 기능을 확장할 수 있음. 코드 재사용: 부모 타입으로 로직을 통일하여 중복을 제거함. |
| 단점 | 가독성 저하: 코드가 추상화되어 있어, 실행 흐름을 눈으로만 쫓을 때 실제 어떤 객체가 실행되는지 한눈에 파악하기 어려울 수 있음 (디버깅 난이도 상승). |
| 필요 조건 | 1. 상속 (Inheritance) 또는 인터페이스 구현 2. 메소드 오버라이딩 (Method Overriding) 3. 업캐스팅 (Upcasting - 부모 타입으로 자식 객체 참조) |
| 예시/비교 | 오버로딩(Overloading): 이름만 같고 매개변수가 다른 것 (컴파일 타임 다형성) 오버라이딩(Overriding): 다형성의 핵심. 부모의 기능을 재정의하는 것 (런타임 다형성) |
Interview Answer Version (면접 답변식 요약)
"다형성이란 하나의 객체나 메소드가 여러 가지 형태를 가질 수 있는 성질을 말합니다. 자바에서는 주로 부모 타입의 참조 변수로 자식 타입의 인스턴스를 참조하는 방식으로 구현됩니다.
다형성을 사용하는 가장 큰 이유는 유지보수성과 확장성 때문입니다. 인터페이스를 통해 역할을 정의하고 구현체를 갈아끼우는 방식으로 설계하면, **기존 코드를 수정하지 않고도 기능을 확장(OCP)**할 수 있어 유연한 시스템을 만들 수 있기 때문입니다."
Practical Tip (사용시 주의할 점 or 활용 예)
1. 실무 활용 코드 (전략 패턴과 유사)
가장 흔한 예시는 Payment (결제) 시스템이나 Repository 교체입니다.
Java
// 1. 역할 정의 (Interface)
interface Payment {
void pay(int amount);
}
// 2. 구현 (Implementation)
class NaverPay implements Payment {
@Override
public void pay(int amount) { System.out.println("네이버페이로 " + amount + "원 결제"); }
}
class KakaoPay implements Payment {
@Override
public void pay(int amount) { System.out.println("카카오페이로 " + amount + "원 결제"); }
}
// 3. 사용 (Client) - 다형성 활용
public class OrderService {
// 구체적인 NaverPay나 KakaoPay가 아닌, 인터페이스 Payment에 의존
private Payment payment;
// 생성자 주입을 통해 구현체를 언제든 갈아끼울 수 있음 (DI의 기초)
public OrderService(Payment payment) {
this.payment = payment;
}
public void processOrder(int amount) {
payment.pay(amount); // 동적 바인딩: 실제 들어온 객체의 pay()가 실행됨
}
}
2. 주의할 점 ("이걸 모르고 쓰면 생기는 문제")
- 다운캐스팅(Downcasting)의 위험: 부모 타입을 자식 타입으로 강제 형변환할 때, 실제 객체 타입이 다르면 ClassCastException이 발생합니다.
- instanceof의 남용: if (obj instanceof KakaoPay) 와 같은 코드가 많다면 다형성을 잘못 쓰고 있는 것입니다. (OCP 위반). 오버라이딩을 통해 해결해야 합니다.
- 필드(변수)는 다형성 적용 X: 오직 메소드만 오버라이딩(다형성)됩니다. 멤버 변수는 참조 변수의 타입(부모)을 따라갑니다.
예상 꼬리질문 정리
- 컴파일 타임 다형성(Overloading)과 런타임 다형성(Overriding)의 차이는 무엇인가요?
- 답변 키워드: 바인딩 시점의 차이. 오버로딩은 컴파일러가 매개변수를 보고 결정, 오버라이딩은 실행 중에 실제 객체를 찾아가 결정(동적 바인딩).
- 추상 클래스와 인터페이스의 차이는 무엇이며, 다형성 관점에서 어떻게 다르게 쓰이나요?
- 답변 키워드: 추상 클래스는 'IS-A'(~이다, 기능 확장), 인터페이스는 'HAS-A' 혹은 'CAN-DO'(~할 수 있다, 행위 규약). 자바 8 이후 인터페이스 기능 강화로 구분 모호해졌으나 목적의 차이가 있음.
- 동적 바인딩(Dynamic Binding)이란 무엇인가요?
- 답변 키워드: 컴파일 시점이 아닌, 프로그램 실행(Runtime) 시점에 참조 변수가 실제로 가리키는 객체의 타입에 따라 호출할 메소드가 결정되는 메커니즘.
'Archive > Daily Dev Q&A' 카테고리의 다른 글
| Deily Dev Q&A: SOLID원칙 (0) | 2025.12.08 |
|---|---|
| Daily Dev Q&A: 오버로딩과 오버라이딩 (0) | 2025.12.05 |
| Daily Dev Q&A: 상속과 컴포지션(Composition)의 차이와, 언제 컴포지션을 사용하는가? (0) | 2025.12.02 |
| Daily Dev Q&A: 객체지향언어란? & 왜 JAVA가 객체지향언어인가? (0) | 2025.12.01 |
| Daily Dev Q&A: 자바의 예외 처리(Exception) 구조 (0) | 2025.11.28 |