2025-08-09 12:46
Tags:
오버라이딩(Overriding) 핸드북
1. 만들어진 이유: 왜 필요한가?
객체 지향 프로그래밍(OOP)의 중요한 특징 중 하나는 다형성(Polymorphism). ‘여러 가지 형태를 가질 수 있는 능력’을 의미. 오버라이딩은 바로 이 다형성을 구현하는 핵심적인 방법.
부모와 자식 관계를 생각하면 이해하기 쉬움. 자식은 부모로부터 유전적 특성(유전자)을 물려받지만, 모든 특성을 똑같이 발현하지는 않음. 예를 들어, 부모가 ‘노래하기’라는 능력을 가지고 있더라도, 자식은 자신만의 스타일로 ‘노래하기’를 할 수 있음.
프로그래밍에서도 마찬가지. **부모 클래스(Superclass)**의 특정 **메서드(Method)**를 **자식 클래스(Subclass)**가 물려받았을 때, 자식 클래스의 상황에 맞게 그 기능을 **재정의(redefine)**하여 사용하고 싶을 때가 있음. 이렇게 부모의 메서드를 자식이 자신만의 방식으로 덮어쓰는 것을 오버라이딩이라고 함.
결론적으로 오버라이딩은 코드의 재사용성을 높이면서도, 각 클래스의 특성에 맞는 유연하고 확장성 있는 설계를 가능하게 하기 위해 만들어짐.
2. 구조: 어떻게 구성되는가?
오버라이딩이 성립하기 위해서는 몇 가지 명확한 규칙을 따라야 함.
성립 조건
-
동일한 이름: 부모 클래스의 메서드와 이름이 완전히 같아야 함.
-
동일한 매개변수: 매개변수의 개수, 타입, 순서가 모두 같아야 함.
-
동일한 반환 타입: 반환하는 데이터 타입이 같아야 함. (※ 공변 반환 타입(Covariant return type)과 같은 예외도 있지만, 기본적으로는 동일해야 함)
추가 규칙
-
접근 제어자: 자식 클래스에서 오버라이딩하는 메서드의 접근 제어자는 부모 클래스의 메서드보다 좁은 범위로 설정할 수 없음. 예를 들어, 부모의 메서드가
public
인데 자식이private
으로 재정의하는 것은 불가능. (더 넓게는 가능:protected
→public
) -
예외 처리: 부모의 메서드보다 더 많은 종류의 예외를 던질 수 없음.
-
final
과static
: 부모 클래스에서final
로 선언된 메서드는 재정의가 불가능하며,static
메서드 또한 오버라이딩의 대상이 아님. (클래스에 속하기 때문)
3. 사용법: 실제 코드 예시
Java 언어를 통해 오버라이딩이 어떻게 사용되는지 살펴보겠음.
상황 설정
-
Animal
(동물)이라는 부모 클래스. 모든 동물은makeSound()
(소리내기) 메서드를 가짐. -
Dog
(개)와Cat
(고양이)이라는 자식 클래스. 각각의 소리는 다르므로makeSound()
를 오버라이딩해야 함.
코드
// 부모 클래스
class Animal {
void makeSound() {
System.out.println("동물이 소리를 냅니다.");
}
}
// 자식 클래스 1
class Dog extends Animal {
// @Override 어노테이션: 컴파일러에게 이 메서드가 오버라이딩된 것임을 알림.
// 만약 오버라이딩 규칙에 맞지 않으면 컴파일 오류를 발생시켜 실수를 방지.
@Override
void makeSound() {
System.out.println("멍멍!");
}
}
// 자식 클래스 2
class Cat extends Animal {
@Override
void makeSound() {
System.out.println("야옹~");
}
}
// 실행 클래스
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog(); // Animal 타입으로 선언했지만, 실제 객체는 Dog
Animal myCat = new Cat(); // Animal 타입으로 선언했지만, 실제 객체는 Cat
myDog.makeSound(); // 출력: 멍멍!
myCat.makeSound(); // 출력: 야옹~
}
}
main 메서드를 보면, myDog
와 myCat
변수는 모두 Animal
타입으로 선언되었음. 하지만 makeSound()
를 호출하면, 변수의 타입이 아닌 실제 메모리에 생성된 객체(new Dog()
, new Cat()
)의 메서드가 호출됨. 이것이 바로 오버라이딩을 통한 다형성의 구현.
4. 심화 내용: 더 알아보기
오버로딩(Overloading) vs. 오버라이딩(Overriding)
두 용어는 비슷해 보이지만 완전히 다름.
구분 | 오버라이딩 (Overriding) | 오버로딩 (Overloading) |
---|---|---|
개념 | 부모 클래스의 메서드를 자식 클래스에서 재정의 | 한 클래스 내에서 이름은 같지만 매개변수가 다른 여러 메서드를 정의 |
관계 | 상속 관계 (부모-자식) | 같은 클래스 내 |
메서드 시그니처 | 이름, 매개변수, 반환 타입 모두 같아야 함 | 이름은 같지만, 매개변수의 개수나 타입이 달라야 함 |
시점 | **런타임(Runtime)**에 호출될 메서드가 결정됨 (동적 바인딩) | 컴파일(Compile) 시점에 호출될 메서드가 결정됨 (정적 바인딩) |
super
키워드
자식 클래스에서 오버라이딩을 할 때, 부모 클래스의 원래 메서드를 호출하고 싶을 수 있음. 이때 super
키워드를 사용.
class Person {
void introduce() {
System.out.println("저는 사람입니다.");
}
}
class Student extends Person {
@Override
void introduce() {
super.introduce(); // 부모 클래스의 introduce() 메서드를 먼저 호출
System.out.println("그리고 학생입니다.");
}
}
// 실행 결과:
// 저는 사람입니다.
// 그리고 학생입니다.
이처럼 super
를 사용하면 부모의 기능을 완전히 덮어쓰는 대신, 기존 기능에 새로운 기능을 추가하는 방식으로 확장할 수 있음.
이 핸드북이 오버라이딩을 이해하는 데 도움이 되었기를 바랍니다.
혹시 오버로딩과의 관계나 super
키워드의 다른 활용법에 대해 더 궁금한 점이 있으신가요?