7.1 OOP II — 상속과 다형성 개요 (Inheritance & Polymorphism Overview)
안내: 이 가이드는 Java 21 버전을 기준으로 작성되었습니다.
지금까지 클래스, 객체, 메서드, 생성자, 오버로딩 등 자바 OOP의 기초를 배웠습니다. 이번 챕터에서는 객체지향 프로그래밍의 진수인 상속(Inheritance), 다형성(Polymorphism), 추상화(Abstraction), 캡슐화(Encapsulation) 를 깊이 있게 다룹니다.
1. OOP II 주제 개요
| 주제 | 키워드 | 핵심 개념 |
|---|---|---|
| 상속 | extends | 부모 클래스의 속성/기능을 물려받아 재사용 |
| 다형성 | @Override, 업/다운캐스팅 | 하나의 코드로 다양한 타입 처리 |
| 추상화 | abstract, interface | 공통 기능만 정의하고 구현을 자식에게 위임 |
| 캡슐화 | private, getter/setter | 데이터를 숨기고 안전한 접근 경로 제공 |
2. 상속(Inheritance) 소개
상속은 기존 클래스(부모/슈퍼 클래스)의 속성(필드)과 기능(메서드)을 새로운 클래스(자식/서브 클래스)가 물려받는 메커니즘입니다.
extends 키워드
// 부모 클래스
public class Animal {
String name;
int age;
void eat() {
System.out.println(name + "이(가) 먹이를 먹습니다.");
}
void sleep() {
System.out.println(name + "이(가) 잠을 잡니다.");
}
}
// 자식 클래스: Animal을 상속
public class Dog extends Animal {
String breed; // 자식만의 추가 필드
void bark() { // 자식만의 추가 메서드
System.out.println(name + "이(가) 멍멍! 짖습니다.");
}
}
상속의 장점
- 코드 재사용: 부모 클래스의 코드를 그대로 활용하므로 중복 작성 불필요
- IS-A 관계: "강아지는 동물이다(Dog IS-A Animal)" — 논리적으로 맞는 관계
- 유지보수: 부모 클래스를 수정하면 자식들에게 자동 반영
- 확장성: 기존 코드 변경 없이 새로운 기능 추가 가능
public class InheritanceTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "바둑이"; // 부모에서 상속받은 필드
dog.age = 3; // 부모에서 상속받은 필드
dog.breed = "진돗개"; // 자식 고유 필드
dog.eat(); // 부모에서 상속받은 메서드: 바둑이이(가) 먹이를 먹습니다.
dog.sleep(); // 부모에서 상속받은 메서드: 바둑이이(가) 잠을 잡니다.
dog.bark(); // 자식 고유 메서드: 바둑이이(가) 멍멍! 짖습니다.
}
}
3. 상속 계층과 Object 클래스
자바의 모든 클래스는 암묵적으로 java.lang.Object 클래스를 최상위 조상으로 가집니다.
Object
└── Animal
├── Dog
│ ├── Poodle
│ └── Husky
├── Cat
└── Bird
├── Parrot
└── Eagle
// 명시적으로 쓰지 않아도 Object를 상속
class Animal extends Object { ... } // 사실 이런 의미
// Object의 주요 메서드: 모든 클래스가 상속받음
Object obj = new Dog();
obj.toString(); // 객체 문자열 표현
obj.equals(obj); // 객체 동등성 비교
obj.hashCode(); // 해시 코드 반환
obj.getClass(); // 런타임 클래스 정보
4. 단일 상속만 가능한 이유 — 다이아몬드 문제
자바는 클래스의 다중 상속을 허용하지 않습니다. 한 클래스는 오직 하나의 부모 클래스만 extends할 수 있습니다.
// 불가! 자바는 다중 상속 금지
// class SmartPhone extends Phone, Camera { } // 컴파일 에러
// 가능: 단일 상속
class SmartPhone extends Phone { ... }
다이아몬드 문제 (Diamond Problem)
Animal
/ \
Dog Cat
\ /
DogCat? ← 다이아몬드 문제!
만약 Dog와 Cat 모두 Animal의 sound() 메서드를 다르게 오버라이딩했을 때, DogCat이 sound()를 호출하면 어느 버전이 실행될지 모호합니다. 이 문제를 방지하기 위해 자바는 다중 상속을 금지합니다.
5. 인터페이스로 다중 구현 가능
다중 상속의 대안으로, 자바는 인터페이스(Interface) 를 통해 다중 구현을 허용합니다.
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
interface Runnable {
void run();
}
// 클래스는 여러 인터페이스를 구현 가능
class Duck extends Animal implements Flyable, Swimmable, Runnable {
@Override
public void fly() { System.out.println("오리가 날아갑니다!"); }
@Override
public void swim() { System.out.println("오리가 헤엄칩니다!"); }
@Override
public void run() { System.out.println("오리가 달립니다!"); }
}
6. 실전 예제: 동물 계층 구조
// 최상위 부모: 모든 동물의 공통 속성과 행동
public class Animal {
protected String name;
protected String sound;
protected int legs;
public Animal(String name, String sound, int legs) {
this.name = name;
this.sound = sound;
this.legs = legs;
}
public void makeSound() {
System.out.println(name + ": " + sound);
}
public void move() {
System.out.println(name + "이(가) " + legs + "개의 다리로 이동합니다.");
}
public void introduce() {
System.out.printf("저는 %s입니다. 다리가 %d개이고 '%s' 소리를 냅니다.%n",
name, legs, sound);
}
}
// 자식 1: 강아지
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name, "멍멍", 4); // 부모 생성자 호출
this.breed = breed;
}
public void fetch() {
System.out.println(name + "이(가) 공을 가져옵니다!");
}
@Override
public void introduce() {
super.introduce();
System.out.println("품종: " + breed);
}
}
// 자식 2: 고양이
public class Cat extends Animal {
private boolean isIndoor;
public Cat(String name, boolean isIndoor) {
super(name, "야옹", 4);
this.isIndoor = isIndoor;
}
public void purr() {
System.out.println(name + "이(가) 그르릉~");
}
@Override
public void introduce() {
super.introduce();
System.out.println("실내 고양이: " + (isIndoor ? "예" : "아니오"));
}
}
// 자식 3: 새
public class Bird extends Animal {
private double wingspan;
public Bird(String name, double wingspan) {
super(name, "짹짹", 2);
this.wingspan = wingspan;
}
public void fly() {
System.out.printf("%s이(가) 날개 %.1fcm로 날아갑니다!%n", name, wingspan);
}
}
// 실행 테스트
public class AnimalTest {
public static void main(String[] args) {
Animal[] animals = {
new Dog("바둑이", "진돗개"),
new Cat("나비", true),
new Bird("참새", 20.5)
};
System.out.println("=== 동물 소개 ===");
for (Animal a : animals) {
a.introduce(); // 다형성: 각 타입의 introduce() 호출
a.makeSound(); // 다형성: 각 타입의 소리
System.out.println("---");
}
// 자식 고유 메서드는 다운캐스팅 필요
Dog dog = (Dog) animals[0];
dog.fetch();
Cat cat = (Cat) animals[1];
cat.purr();
Bird bird = (Bird) animals[2];
bird.fly();
}
}
출력:
=== 동물 소개 ===
저는 바둑이입니다. 다리가 4개이고 '멍멍' 소리를 냅니다.
품종: 진돗개
바둑이: 멍멍
---
저는 나비입니다. 다리가 4개이고 '야옹' 소리를 냅니다.
실내 고양이: 예
나비: 야옹
---
저는 참새입니다. 다리가 2개이고 '짹짹' 소리를 냅니다.
참새: 짹짹
---
바둑이이(가) 공을 가져옵니다!
나비이(가) 그르릉~
참새이(가) 날개 20.5cm로 날아갑니다!
7. 챕터 7에서 배울 내용 미리보기
| 섹션 | 주제 | 핵심 내용 |
|---|---|---|
| 7.2 상속 | extends, super(), 메서드 오버라이딩 | 부모-자식 관계, 생성자 체이닝 |
| 7.3 다형성 | 업캐스팅, 다운캐스팅, instanceof | 유연한 코드 작성, 동적 바인딩 |
| 7.4 추상화 | abstract, interface | 설계 도구, 계약 기반 프로그래밍 |
| 7.5 캡슐화 | private, getter/setter, Record | 데이터 보호, 불변 클래스 |
| 7.6 super | super.필드, super.메서드(), super() | 부모 멤버 접근, 생성자 연쇄 |
왜 이 개념들이 중요한가?
상속, 다형성, 추상화, 캡슐화는 단순한 문법이 아닙니다. 이 개념들은 디자인 패턴, SOLID 원칙, Spring Boot 프레임워크 등 현업에서 사용하는 모든 고급 기술의 기반이 됩니다. 이 챕터를 충분히 이해하면 프로급 자바 개발자로 도약하는 발판이 마련됩니다.
요약
| 개념 | 핵심 |
|---|---|
| 상속 | extends로 부모의 필드·메서드 재사용 |
| Object 클래스 | 모든 클래스의 최상위 조상 |
| 단일 상속 | 다이아몬드 문제 방지, 클래스는 하나만 상속 |
| 다중 구현 | implements로 여러 인터페이스 구현 가능 |
| IS-A 관계 | 상속은 논리적 계층 구조를 반영해야 함 |