자바 상속에서 꼭 짚고 넘어가야 할 핵심
1. 파생 클래스 생성 시 부모 클래스도 함께 생성된다
- 자식 클래스를 new 하면, 부모 클래스의 생성자가 먼저 호출되고, 그 다음 자식 클래스 생성자가 실행됩니다.
- 이 과정은 콜 스택(call stack)에 생성자 호출이 쌓였다가 차례대로 실행되는 것과 비슷합니다.
📌 예시
class Parent {
Parent() {
System.out.println("Parent 생성자");
}
}
class Child extends Parent {
Child() {
System.out.println("Child 생성자");
}
}
public class Main {
public static void main(String[] args) {
new Child();
}
}
출력:
Parent 생성자
Child 생성자
2. 상속은 코드 흐름을 2차원적으로 만든다
- 단순히 “현재 클래스 안의 코드 흐름”만 존재하는 게 아니라,
- 부모 → 자식 관계로 흐름이 확장되며, 현재 작성자와 미래 작성자가 “대화”할 수 있게 됩니다.
- 이때 중요한 것은 메서드 오버라이딩을 통해, 부모가 정의한 틀을 자식이 적절히 확장할 수 있다는 점입니다.
➡️ 즉, 상속은 단순히 재사용을 위한 게 아니라, “현재와 미래가 소통하는 구조”라는 점을 이해해야 합니다.
3. 다중 상속은 불가능, 인터페이스는 다중 구현 가능
- 자바 클래스는 단일 상속만 지원합니다. (부모는 하나만 가질 수 있음)
- 하지만 인터페이스는 여러 개 구현 가능 → “행위 계약”을 다중으로 가져갈 수 있음.
📌 예시
interface Flyable { void fly(); }
interface Swimmable { void swim(); }
class Duck implements Flyable, Swimmable {
public void fly() { System.out.println("날다"); }
public void swim() { System.out.println("헤엄치다"); }
}
4. 파생 클래스에서 부모 클래스의 필드를 다시 정의하지 말 것
- 자식 클래스에서 부모 클래스의 필드와 동일한 이름으로 변수를 다시 정의하는 것은 혼란만 불러일으키는 행위입니다.
- 유지보수성도 떨어지고, 코드 읽는 사람에게 혼동을 줍니다.
➡️ 널널한 개발자님은 이걸 “바보짓”이라고까지 표현합니다.
즉, 상속은 확장을 위한 것이지, 부모의 속성을 덮어씌우는 장치가 아님을 명심해야 합니다.
5. “현재 vs 미래”의 관점에서 상속을 바라보기
- 부모 클래스는 현재 시점에서 작성하는 코드
- 자식 클래스는 미래 시점에 확정되는 코드
➡️ 따라서 부모 클래스를 작성할 때는, 미래의 확장을 고려해야 합니다.
예를 들어, 내가 6개월 뒤 다시 코드를 봤을 때, 혹은 다른 개발자가 이 클래스를 이어받았을 때도 의도를 쉽게 파악할 수 있어야 합니다.
6. 상속의 깊이는 얕게 가져가라
- 상속이 상속을 타고 이어지는 구조(증조클래스, 고조클래스…)는 바람직하지 않습니다.
- 그렇게 되면 “위로 몇 단계를 타고 올라가야 이 클래스의 동작을 알 수 있는지” 복잡해지고, 유지보수가 어려워집니다.
➡️ 실무에서는 상속의 깊이를 얕게 가져가고, 합성(Composition) 을 활용하는 것이 더 좋은 경우가 많습니다.
* 상속으로만 해결하려는 경우 (안 좋은 예)
class Engine {
void start() { System.out.println("엔진 가동"); }
}
class Car extends Engine { // Car가 Engine을 상속
void drive() { System.out.println("운전 시작"); }
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.start(); // Engine 기능 직접 사용
car.drive();
}
}
문제점:
- Car 가 Engine의 하위 타입처럼 보이지만 사실 논리적으로 “Car는 Engine이다”라는 관계는 이상합니다.
- 나중에 Car 말고 Truck, Bus 등 여러 차종이 생기면 상속 구조가 불필요하게 깊어지고 꼬입니다.
2. 합성을 활용하는 경우 (더 나은 설계)
class Engine {
void start() { System.out.println("엔진 가동"); }
}
class Car {
private Engine engine = new Engine(); // 합성: Car 안에 Engine 포함
void drive() {
engine.start(); // Engine 기능 사용
System.out.println("운전 시작");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.drive();
}
}
장점:
- Car는 Engine을 “가지고 있다(has-a)” 관계 → 현실 모델링에도 적합
- 나중에 Car에 다른 종류의 Engine(예: 전기 엔진, 하이브리드 엔진)을 넣을 수도 있음
- 상속 깊이가 늘어나지 않고, 조합만 바꿔서 확장 가능
3. 실무에서 합성이 선호되는 이유
- 상속: “is-a” 관계 → 강한 결합, 부모에 의존
- 합성: “has-a” 관계 → 약한 결합, 유연한 확장
➡️ 합성은 부품 교체처럼 객체 조합으로 기능을 확장할 수 있어서, 서비스 코드가 커질수록 훨씬 관리하기 쉽습니다.
핵심 요약
- 자식 생성자는 부모 생성자를 거쳐 실행된다 (콜 스택 이해 필요).
- 상속은 코드 흐름을 2차원적으로 만들어 “현재와 미래의 대화”가 가능해진다.
- 클래스는 단일 상속만 가능, 인터페이스는 다중 구현 가능하다.
- 부모 필드를 자식에서 다시 정의하는 것은 절대 피해야 한다.
- 부모 클래스는 현재, 자식 클래스는 미래 → 미래 확장을 고려한 코드 작성이 필수.
- 상속의 깊이는 얕게, 불필요한 다단계 상속은 지양하라.
'자바 > 자바' 카테고리의 다른 글
| (자바)JVM, 클래스 로딩, 런타임 메모리 구조 정리 *널널한 개발자 (0) | 2025.09.09 |
|---|---|
| (자바) 자바 상속, 다형성, 추상화 *널널한 개발자 (0) | 2025.09.08 |
| (자바) 인프런 강의(자바 ) *널널한 개발자 (0) | 2025.09.02 |
| 인프런 강의 정리_ JAVA 성능 튜닝과 트러블 슈팅 * 휴먼넷 (1) | 2025.08.31 |
| (자바)자바 제너릭 (Generics) 정리 (0) | 2025.08.21 |