다형성은 앞서 적은 객체지향(OOP)의 꽃이라고 불릴 만큼 중요한 개념입니다. 면접관들은 단순히 정의를 아는 것을 넘어, 이를 '왜' 사용하고 '어떻게' 실무에 적용하는지를 궁금해서 질문했을 거라고 생각합니다.
다형성
다형성이란 하나의 객체나 메서드가 여러 가지 다른 형태를 가질 수 있는 성질을 의미한다.
프로그래밍 관점에서는 부모 클래스(또는 인터페이스) 타입의 참조 변수로 자식 클래스의 인스턴스를 참조할 수 있다.
주요 특징 및 구현 방식
오버라이딩 (Overriding)
개념: 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것이다. (런타임 다형성)
핵심: 메서드 이름, 파라미터, 리턴 타입이 모두 같아야 한다.
class Animal {
void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
class Dog extends Animal {
@Override // 어노테이션을 붙여주는 것이 관례 (컴파일러 체크 도움)
void makeSound() {
System.out.println("멍멍!"); // 부모의 동작을 변경함
}
}
부모 클래스 Animal의 makeSound 메서드를 재정의하여 출력할 문장을 변경한다.
오버라이딩은 동적 바인딩이다.
런타임 시점에서 JVM이 판단하는데, 컴파일러가 실제 어떤 객체가 들어올지 모르기 때문에 보류하고 넘긴다.
실행 중에 JVM이 지금 들어오는 객체를 판단하고 그때 연결한다.
오버로딩 (Overloading)
개념: 같은 이름의 메서드를 매개변수의 개수나 타입을 다르게 하여 여러 개 정의하는 것이다. (컴파일 타임 다형성)
핵심: 메서드 이름은 같지만, 파라미터 시그니처가 달라야 한다.
class Calculator {
// 정수 더하기
int add(int a, int b) {
return a + b;
}
// 실수 더하기 (이름은 add로 같지만, 파라미터 타입이 다름)
double add(double a, double b) {
return a + b;
}
// 세 개 더하기 (이름은 add로 같지만, 파라미터 개수가 다름)
int add(int a, int b, int c) {
return a + b + c;
}
}
add라는 메서드는 같지만, 매개변수의 타입이나 개수에 따라 서로 다른 메서드라고 컴파일러 javac가 판단한다.
오버로딩은 정적 바인딩이다.
정적 바인딩은 컴파일 시점에서 javac 컴파일러가 코드를 읽으면서 실행할 주소를 미리 고정해 버리는 것으로 속도가 동적 바인딩보다 빠르다.
그밖에 static 메서드, private 메서드, final 메서드가 있다.
실무 적용 방법
- If/Else 문 제거: 수많은 if type == 'A' 식의 분기문을 다형성으로 대체하여 코드를 깔끔하게 만든다. (Refactoring Strategy)
- 플러그인 아키텍처: 새로운 기능을 추가할 때 기존 코드를 건드리지 않고 '끼워 넣는' 방식의 설계에 사용된다.
- 테스트 용이성: 실제 객체 대신 Mock 객체(가짜 객체)를 주입하여 테스트하기 쉽다.
과한 다형성을 사용 시 주의할 점
- 과도한 추상화: 인터페이스와 상속 계층이 너무 깊어지면, "도대체 실제 코드는 어디 있어?"라며 코드를 파악하기 힘들어진다.
- YAGNI (You Ain't Gonna Need It): 당장 하나의 구현체만 필요한데, 미래를 대비한다며 무조건 인터페이스부터 만드는 것은 오버 엔지니어링이 될 수 있다.
면접 답변식 요약
다형성은 하나의 인터페이스로 여러 형태의 구현체를 다룰 수 있는 성질을 말합니다.
핵심은 부모 타입의 참조 변수로 자식 객체를 다루는 것인데, 이를 통해 클라이언트 코드를 변경하지 않고도 기능을 확장할 수 있다는 점이 가장 큰 장점입니다.
예를 들어, 결제 시스템을 만들 때 if문으로 결제 수단을 구분하는 대신, 공통된 Payment 인터페이스를 상속받아 pay() 메서드를 오버라이딩하면, 나중에 새로운 결제 수단이 추가되어도 기존 로직을 수정할 필요가 없어 유지보수성과 확장성이 뛰어난 코드를 작성할 수 있습니다.
공부하다가 궁금한 부분
소프트웨어 개발 3대 원칙
KISS, YAGNI, DRY이 있으며, 그 중 YAGNI(You Ain't Gonna Need it)는 "지금 필요 없는 기능을 만들지 말라!"라는 의미이다.
- 지금 필요 없는 기능을 개발한다면 나중에 필요없어질 수도 있다.
- 이는 개발 비용, 유지보수 비용, 더 나아가서 수리 비용까지 야기할 수 있다.
- 불필요하게 개발하는 작업이 다른 코드에도 영향을 미칠 수 있다.
즉, 코드의 가독성을 높이고 복잡도를 줄여 유지보수하기 쉽게 만들기 위한 원칙이다.
KISS(Keep It Simple, Stupid)는 "심플하고 멍청하게 유지하라!"라는 의미로, 주석이 없어도 함수 이름, 매개 변수, 코드를 통해 한 번에 이해가 가능한 수준으로 작성해야 한다.
SOLID 중 단일 책임 원칙처럼, 각 기능은 한 기능만을 담당하는 것이 좋고 한 가지의 책임만을 가져야 한다.
즉, 불필요한 기능 구현에 시간을 낭비하지 않고, 현재 꼭 필요한 기능에 집중하여 개발 시간과 노력을 절약한다.
DRY(Don't Repeat Yourself)는 "중복을 피해라!"라는 의미로 같은 정보나 로직이 여러 곳에 반복되는 것을 피하라는 원칙을 의미한다.
즉, 코드 재사용성을 높이고, 한 곳을 수정하면 모든 중복된 부분을 수정할 필요가 없도록 하여 유지보수를 용이하게 한다.
'Daily Dev Q&A 정리 템플릿' 카테고리의 다른 글
| 25.12.07 SOLID원칙에 대해 (1) | 2025.12.07 |
|---|---|
| 25.12.04 오버로딩과 오버라이딩에 대해서 구체적으로 설명하라는 질문에 대한 대답은? (0) | 2025.12.05 |
| 25.12.02 상속과 컴포지션(Composition)의 차이와, 언제 컴포지션을 사용할까? (0) | 2025.12.02 |
| 25.12.01 객체지향 언어 개념과 자바는 어떤 언어인가 (0) | 2025.12.01 |
| 25.11.28 자바의 예외 처리(Exception)에 대하여 (0) | 2025.11.30 |