상속 (Inheritance)

핵심 인사이트 (3줄 요약)

  1. 본질: 상속은 부모 클래스(Superclass)의 속성과 메서드를 자식 클래스(Subclass)가引き继ariatることでコードを再利用性を高めるOOP의 핵심 특성이다.
  2. 가치: 공통 기능을 부모 클래스에 정의하고, 자식 클래스는差异化된 기능만 정의함으로써, 코드 중복을 줄이고 클래스 간 계층 구조를 형성하여 프로그램의構造를 명확히 한다.
  3. 융합: 상속은 DDD의 도메인 모델링에서 계층 구조 표현, GoF 디자인 패턴에서 템플릿 메서드 패턴 등의 기반으로 활용된다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

  • 개념: 상속은 객체 지향 프로그래밍에서 기존 클래스(부모 클래스, Superclass)의 属性とメソッドを新しいクラス(子クラス、Subclass)に引き継ぐ仕組み이다。자식 클래스는 부모 클래스의 모든 public, protected成员을继承하고, 자신만의 새로운 属性とメソッドを追加하거나、부모의 메서드를再定義(オーバーライド)할 수 있다.

  • 필요성: 만약 상속이 없다면, 공통 기능이 필요한 각 클래스에서同一 코드를 반복해서 작성해야 한다. 예를 들어, 동물 클래스를 만들어야 하는데 새, 고양이, 개가 각각 같은 기능(이름, age)을各자정의하면, 나중에动物共通 기능 변경이 필요할 때 모든 클래스를 수정해야 하는 불편이 발생한다. 상속을 사용하면 공통 기능을 Animal이라는 부모 클래스에 정의하고, 자식 클래스는各自固有 기능만 추가하면 된다.

  • 💡 비유: 상속은 유전과 같습니다. родитель가 키, 눈 색 등의特性(속성)을 자식에게 전달하면, 자식은 родитель의特性를 공유하면서도 자신만의 특성(예:兄は暗い肌, 동생은 밝은肤色)을 갖게 됩니다.

  • 등장 배경 및 발전 과정:

    1. 1967년 Simula: Ole-Johan Dahl과 Kristen Nygaard가 Simula에서 최초로 상속 개념을 도입했다.

    상속의 계층 구조를 시각화하면 다음과 같다.

    ┌─────────────────────────────────────────────────────────────────────┐
    │                    상속의 계층 구조 (继承 계층)                        │
    ├─────────────────────────────────────────────────────────────────────┤
    │
    │  [부모 클래스 (Superclass)]                                         │
    │
    │          ┌─────────────────────────────┐                         │
    │          │         Animal              │                         │
    │          │─────────────────────────────│                         │
    │          │ # name: String             │  ← protected: 자식 접근   │
    │          │ # age: int                  │                         │
    │          │─────────────────────────────│                         │
    │          │ + eat(): void                │                         │
    │          │ + sleep(): void              │                         │
    │          │ + makeSound(): void          │  ← 하위에서 오버라이드   │
    │          └──────────────┬──────────────┘                         │
    │                         │                                         │
    │          ┌──────────────┴──────────────┐                         │
    │          │                              │                          │
    │          ▼                              ▼                          │
    │  ┌─────────────────────┐    ┌─────────────────────┐           │
    │  │        Dog          │    │        Cat          │           │
    │  │─────────────────────│    │─────────────────────│           │
    │  │ + breed: String     │    │ + indoor: boolean   │           │
    │  │─────────────────────│    │─────────────────────│           │
    │  │ + fetch(): void     │    │ + groom(): void     │           │
    │  │ + makeSound(): void │    │ + makeSound(): void │           │
    │  └─────────────────────┘    └─────────────────────┘           │
    │                                                                     │
    │  Dog와 Cat는 Animal의 속성(name, age)과 메서드(eat, sleep)를      │
    

│ │ 상속받고,各自 고유한 속성(breed, indoor)과 메서드(fetch, groom)를│ │ │ 추가했으며, makeSound()는各自 오버라이드했다. │ │ │ │ │ │ 사용 예: │ │ │ Animal a1 = new Dog("Max", 3); │ │ │ Animal a2 = new Cat("Luna", 2); │ │ │ │ │ │ a1.makeSound(); // "멍멍!" (Dog의 오버라이드된 메서드) │ │ │ a2.makeSound(); // "야옹!" (Cat의 오버라이드된 메서드) │ │ │ │ └─────────────────────────────────────────────────────────────────────┘


**[다이어그램 해설]** Animal이 부모 클래스로, name, age 속성과 eat(), sleep(), makeSound() 메서드를 가진다. Dog와 Cat가 Animal을 상속받는다. Dog는 breed 속성과 fetch() 메서드를 추가하고, makeSound()를 "멍멍!"으로 오버라이드한다. Cat는 indoor 속성과 groom() 메서드를 추가하고, makeSound()를 "야옹!"으로 오버라이드한다. 중요한 점은 Animal 타입의 참조 변수(a1, a2)에 각각 Dog와 Cat 객체를 대입할 수 있다는 것이다. a1.makeSound()은 Animal에 정의되어 있지만, 실제로는 Dog에서 오버라이드된 "멍멍!"이 호출된다. 이것이 다형성(Polymorphism)의 핵심이다.

2. **1990년대 Java의 상속 체계 확립**: Java가 extends 키워드로 단일 상속을 지원하기 시작했다.

- **📢 섹션 요약 비유**: 상속은 **동물원 사육사**와 같습니다. 모든 동물(자식 클래스)은"먹는다", "잠든다"(부모 메서드)는共同的인 기능을 공유하지만, "운다"(오버라이드)는 방식은種(자식 클래스)마다 다릅니다.

---

## Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

### 상속의 유형

| 유형 | 설명 | 예시 언어 |
|:---|:---|:---|
| **단일 상속** | 자식이 부모를 하나만 상속 | Java, C#, Kotlin |
| **다중 상속** | 자식이 여러 부모를 상속 | C++, Python |
| **계층적 상속** | 하나의 부모가 여러 자식을 가짐 | Java |
| **다단계 상속** | 조상 → 부모 → 자식의 체인 | Java, C++ |

---

### 메서드 오버라이딩과 동적 바인딩

자식 클래스에서 부모의 메서드를 다시 정의하는 오버라이딩과, 어떤 메서드가 호출될지 런타임에 결정되는 동적 바인딩의 메커니즘을 시각화한다.

```text
┌─────────────────────────────────────────────────────────────────────┐
│                    메서드 오버라이딩과 동적 바인딩                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [오버라이딩 기본 구조]                                              │
│                                                                     │
│  // 부모 클래스                                                      │
│  public class Animal {                                              │
│      public void makeSound() {                                     │
│          System.out.println("... (原始 소리)");                      │
│      }                                                             │
│  }                                                                 │
│                                                                     │
│  // 자식 클래스 - 오버라이딩                                         │
│  public class Dog extends Animal {                                  │
│      @Override                                                     │
│      public void makeSound() {                                     │
│          System.out.println("멍멍!");                              │
│      }                                                             │
│  }                                                                 │
│                                                                     │
│  // 자식 클래스 - 오버로딩 (Overloading)                            │
│  public class Animal {                                              │
│      public void makeSound(String mood) {  // 메서드 이름 같지만     │
│          if (mood.equals("happy")) {                              │
│              System.out.println("밝게 짖다");                      │
│          } else {                                                  │
│              System.out.println("조용히 짖다");                     │
│          }                                                         │
│      }                                                             │
│  }                                                                 │
│                                                                     │
│  ※ 오버라이딩: 메서드 시그니처 동일 (파라미터 같음)                   │
│  ※ 오버로딩: 메서드 이름 같지만 파라미터 다름                         │
│                                                                     │
│  [동적 바인딩 (Dynamic Binding)]                                    │
│                                                                     │
│  Animal animal = new Dog();  // 부모 타입, 자식 객체                  │
│  animal.makeSound();      // "멍멍!" 호출                           │
│                                                                     │
│  JVM의 메서드 호출 과정:                                             │
│  ┌─────────────────────────────────────────────────────────────┐  │
│  │  1. animal의 실제 타입(실제 객체)이 Dog임을 확인               │  │
│  │  2. Dog 클래스에서 makeSound() 검색                           │  │
│  │  3. Dog에 오버라이드된 makeSound()이 있으면 해당 메서드 호출    │  │
│  │  4. 없으면 부모 Animal의 makeSound() 호출                     │  │
│  └─────────────────────────────────────────────────────────────┘  │
│                                                                     │
│  ※ 런타임에 결정 → "동적" 바인딩                                     │
│                                                                     │
│  [다형성의 활용: 배열과 컬렉션]                                       │
│                                                                     │
│  Animal[] animals = new Animal[3];                                 │
│  animals[0] = new Dog();                                            │
│  animals[1] = new Cat();                                           │
│  animals[2] = new Bird();                                           │
│                                                                     │
│  for (Animal animal : animals) {                                   │
│      animal.makeSound(); // 각 객체의 실제 타입에 맞는 소리 출력     │
│  }                                                                 │
│  // 출력: 멍멍! 야옹! 짹짹!                                          │
│                                                                     │
│  핵심 규칙:                                                         │
│  1. @Override 어노테이션으로 오버라이드 명시적 표시                    │
│  2. private 메서드는 오버라이드 불가                                  │
│  3. final 메서드는 오버라이드 불가                                    │
│  4. static 메서드는 오버라이드가 아닌 숨김 (Shadows)                  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

[다이어그램 해설] 메서드 오버라이딩은 자식 클래스에서 부모의 메서드를 다시 정의하는 것이다. @Override 어노테이션은 컴파일러에게 "이 메서드는 오버라이드이다"를 알려주어, 실수로 시그니처를 잘못 쓰면 컴파일 에러가 발생하게 한다. 동적 바인딩은 animal.makeSound() 호출 시, animal 변수의 컴파일 타임 타입(Animal)ではなく、런타임에 실제 객체(Dog)의 메서드가 호출되는 메커니즘이다. 이는 다형성의 핵심으로, 부모 타입의 참조 변수로 자식 객체를 참조하더라도, 실제 호출되는 메서드는 자식 클래스에서 오버라이드된 메서드다. 이를 통해 for 루프에서 animal.makeSound()를 호출하면, 각 객체의 실제 타입에 맞는 소리가 출력된다.

  • 📢 섹션 요약 비유: 동적 바인딩은 원어로서 명령을 내리는 것과 같습니다. "叫んで!"( makeSound())라고 명령하면, 개(Dog)는 "멍멍!" 하고, 고양이(Cat)는 "야옹!" 하고, 새(Bird)는 "짹짹!" 하고各自叫びます. 명령은同一하지만, 각자의叫声(실행)은 다릅니다.

Ⅲ. 융합 비교 및 다각도 분석

비교: 상속 vs 합성 (Composition)

구분상속 (Inheritance)합성 (Composition)
관계"is-a" (분류)"has-a" (소유)
결합도높음 (부모 변경 시 자식 영향)낮음 (구성 요소 변경 영향 적음)
유연성빌드 타임에 결정 (고정)런타임에 교체 가능
주 사용처명확한 분류 체계기능 조합/확장
┌─────────────────────────────────────────────────────────────────────┐
│                    상속 vs 합성 비교                                 │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  [상속 (is-a)]                                                    │
│                                                                     │
│  public class Car extends Vehicle {  // Car은 Vehicle이다           │
│      // Vehicle의 속성/메서드를 그대로 사용                          │
│      // + Car만의 속성/메서드 추가                                  │
│  }                                                                 │
│                                                                     │
│  장점: 간단한 경우 강력함                                            │
│  단점:爹更改시 子가 영향接受, 빌드 타임에 결정되어灵活성 부족          │
│                                                                     │
│  [합성 (has-a)]                                                    │
│                                                                     │
│  public class Car {                                                │
│      private Engine engine;  // Car은 Engine을 소유한다             │
│      private Wheel[] wheels;                                       │
│                                                                     │
│      public Car(Engine engine) {                                   │
│          this.engine = engine;                                     │
│      }                                                             │
│                                                                     │
│      public void start() {                                         │
│          engine.start(); // Engine에 작업 위임                      │
│      }                                                             │
│  }                                                                 │
│                                                                     │
│  장점: 런타임에 엔진 교체 가능, 결합도 낮음                           │
│  단점: 위임 코드 필요 ( Boilerplate)                                │
│                                                                     │
│  [Effective Java의 조언]                                            │
│  "상속보다 합성을 선호하라"                                          │
│  - 상속은 강력한 결합도를 초래                                       │
│  - 합성은 필요한 경우에만 사용                                        │
│  - 특히 다른 패키지의 클래스를 상속할 때는 주의                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

[다이어그램 해설] 상속은 "is-a" 관계를 표현할 때 적합하다. Car is a Vehicle(자동차는 탈것이다)이므로 상속이 자연스럽다. 그러나 합성은 "has-a" 관계를 표현할 때 적합하다. Car has an Engine(자동차는 엔진을 소유한다)이라는 관계에서, Engine을 Car 내부에 포함시키는 것이 합성이다. Effective Java의 저자 Joshua Bloch는 "상속보다 합성을 선호하라"고 조언한다. 그 이유는 상속이 빌드 타임에 고정되어灵活性が低く, 부모 클래스의 변경이 자식 클래스에连锁적으로 영향을 미칠 수 있기 때문이다. 특히 다른 패키지의 클래스를 상속하면, 부모 클래스의 내부 구현에 your가 의존하게 되어, 부모 클래스의 구현이 변경되면 자식 클래스도影響받을 수 있다.

  • 📢 섹션 요약 비유: 상속은 아버지의性を 직접 물려받는 것과 같습니다.父亲的 DNA(부모 코드)를 그대로引き继ariatて,父亲的技能을基本として追加的な技能을 가집니다. 합성은 팀원을 팀에 모은 것과 같습니다. 각 팀원(합성 요소)이 각자의 기능을提供하지만, 팀원이바쁘면直接 대처할 수 있고, 필요하다면 팀원을 교체할 수도 있습니다.

Ⅳ. 실무 적용 및 기술사적 판단

실무 시나리오

  1. 시나리오 — 결제 시스템: 결제 수단별로 PaymentGateway를 상속하여 CreditCardPayment, KakaoPayment, NaverPayment 등을 구현할 수 있다. 각 결제 수단의 차이점은 makePayment() 메서드의 오버라이딩으로 표현한다.

도입 체크리스트

  • 기술적: "is-a" 관계가 확실한가? 상속 대신 합성이 더 적절한 상황은 아닌가?
  • 운영·보안적: 자식 클래스에서 부모 메서드를 오버라이드할 때, 부모의 contract를尊重하는가?

안티패턴

  • 상속 남용: "has-a" 관계인데도 상속을 사용하여 결합도가 높아지는 안티패턴.

  • 📢 섹션 요약 비유: 상속 남용은 배우자가官公庁PROC에 상속하는 것과 같습니다.本来 composable한 구성 요소를"분류"强行하면,官公ulen의 업무 방식이丸ごと組み込まれて 버려서灵活性と再利用性が失われます。


Ⅴ. 기대효과 및 결론

미래 전망

  • 위임(Delegation)과 믹스인(Mixin): 상속보다 더 유연한 코드 재사용 기법으로, 위임은 합성의 일종이고, 믹스인은 클래스에 기능을 섞어 넣는 기법이다.

📌 관련 개념 맵 (Knowledge Graph)

개념 명칭관계 및 시너지 설명
오버라이딩 (Overriding)자식 클래스에서 부모의 메서드를 다시 정의하는 것이다.
동적 바인딩 (Dynamic Binding)런타임에 실제 객체 타입에 따라 호출될 메서드가 결정되는 메커니즘이다.
합성 (Composition)객체 내부에 다른 객체를 포함하여 "has-a" 관계를 표현하는 기법이다.
다형성 (Polymorphism)상속과 오버라이딩을 통해 동일한 메서드 호출에 대해 다른 동작을 하는 능력이다.

👶 어린이를 위한 3줄 비유 설명

  1. 상속은 아버지와 아들의类似的关系와 같습니다. 아들은 아버지에게서眼睛の色や키などの特徴(속성)를 물려받고, 같은技能(메서드)도 쓸 수 있어요.
  2. 그런데 아들은 아버지와 다른 점(예: 아버지는 Progammer, 아들은 musician)도 가지고 있죠. 이것이 오버라이딩으로, 같은 이름의 기능을 다르게 구현한 것이에요.
  3. 만약 아들이 아버지의职业까지 그대로继承하면(강한 결합), 아버지가换一个职业하면 아도被迫跟着하지만, 技能만 위임받는다면(합성), 아는 아버지와 다른 직업でもbeanspruch并行할 수 있어요!