Java 상속을 깊이 있게 탐구합니다. 원리, 실전 예제, 더 깔끔하고 재사용 가능한 코드를 위한 적용 방법을 이해하세요.
상속은 기존 클래스를 기반으로 새 클래스를 생성할 수 있게 하는 객체 지향 프로그래밍의 초석입니다.
파생 클래스를 하위 클래스(자식 또는 파생), 기반 클래스를 슈퍼클래스(부모 또는 기반)라고 합니다. extends 키워드가 이 관계를 수행합니다.
class Animal { // 메서드와 필드 } class Dog extends Animal { // Dog는 Animal의 필드와 메서드를 상속 }
여기서 Dog는 Animal의 모든 멤버를 상속받습니다. 하위 클래스가 부모의 기능을 얻게 됩니다.
예제 1: 기본 상속
class Animal { String name; public void eat() { System.out.println("I can eat"); } } class Dog extends Animal { public void display() { System.out.println("My name is " + name); } } class Main { public static void main(String[] args) { Dog labrador = new Dog(); labrador.name = "Rohu"; labrador.display(); labrador.eat(); } }
출력
My name is Rohu I can eat
이 예제에서 Dog는 Animal로부터 name과 eat()을 상속받아 Dog 객체가 직접 접근할 수 있습니다.
is-a 관계
Java의 상속은 is-a 관계를 나타냅니다: 하위 클래스는 슈퍼클래스의 특수한 형태입니다.
자동차는 탈것이다 (Car is a Vehicle)
오렌지는 과일이다 (Orange is a Fruit)
외과의사는 의사다 (Surgeon is a Doctor)
개는 동물이다 (Dog is an Animal)
이런 자연스러운 계층 구조가 상속 사용을 정당화합니다.
메서드 오버라이딩
하위 클래스가 슈퍼클래스와 같은 시그니처의 메서드를 정의하면, 하위 클래스 메서드가 슈퍼클래스 버전을 오버라이딩합니다. 이는 런타임 다형성을 가능하게 합니다.
예제 2: eat() 오버라이딩
class Animal { public void eat() { System.out.println("I can eat"); } } class Dog extends Animal { @Override public void eat() { System.out.println("I eat dog food"); } public void bark() { System.out.println("I can bark"); } } class Main { public static void main(String[] args) { Dog labrador = new Dog(); labrador.eat(); labrador.bark(); } }
출력
I eat dog food I can bark
labrador에서 eat() 호출은 Dog의 오버라이딩된 메서드로 해석됩니다.
팁:@Override 애너테이션은 선택 사항이지만 강력히 권장됩니다. 컴파일러가 실제로 메서드를 오버라이딩하는지 확인해 줍니다.
super로 슈퍼클래스 접근
오버라이딩 시 super 키워드로 부모 구현을 호출할 수 있습니다.
예제 3: eat()에서 super 사용
class Animal { public void eat() { System.out.println("I can eat"); } } class Dog extends Animal { @Override public void eat() { super.eat(); System.out.println("I eat dog food"); } public void bark() { System.out.println("I can bark"); } } class Main { public static void main(String[] args) { Dog labrador = new Dog(); labrador.eat(); labrador.bark(); } }
출력
I can eat I eat dog food I can bark
여기서 super.eat()이 Animal에 정의된 메서드를 먼저 호출한 후 하위 클래스 로직을 실행합니다.
마찬가지로 super로 슈퍼클래스 생성자도 호출할 수 있습니다.
상속에서 Protected 멤버
protected로 표시된 필드나 메서드는 패키지에 관계없이 하위 클래스에서 접근 가능합니다.
예제 4: Protected 멤버 접근
class Animal { protected String name; protected void display() { System.out.println("I am an animal."); } } class Dog extends Animal { public void getInfo() { System.out.println("My name is " + name); } } class Main { public static void main(String[] args) { Dog labrador = new Dog(); labrador.name = "Rocky"; labrador.display(); labrador.getInfo(); } }
출력
I am an animal. My name is Rocky
하위 클래스는 protected 필드 name을 자유롭게 읽고 수정할 수 있으며 display()를 호출할 수 있습니다.
왜 상속을 사용하는가?
코드 재사용: 자식 클래스가 부모의 모든 public 및 protected 멤버를 자동으로 상속
다형성: 오버라이딩으로 동적 메서드 디스패치 가능, 런타임 다형성의 핵심 기능
상속의 종류
Java는 다섯 가지 개념적 상속 모델을 지원하며, 각기 다른 시나리오에 유용합니다.
1. 단일 상속 (Single Inheritance)
클래스가 하나의 슈퍼클래스만 확장합니다.
2. 다단계 상속 (Multilevel Inheritance)
하위 클래스가 또 다른 클래스의 슈퍼클래스가 되어 체인을 형성합니다.
3. 계층적 상속 (Hierarchical Inheritance)
여러 하위 클래스가 단일 슈퍼클래스를 상속합니다.
4. 다중 상속 (Multiple Inheritance)
Java는 클래스가 두 개 이상의 클래스를 확장하는 것을 허용하지 않지만, 인터페이스로 이 패턴을 구현할 수 있습니다.