Menu
Coddy logo textTech

메서드 오버라이딩 (런타임)

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 29번째.

메서드 오버로딩(method overloading)이 컴파일 타임에 결정되는 반면, 메서드 오버라이딩(method overriding)런타임 다형성(run-time polymorphism)을 보여줍니다. 여기서 Java는 변수의 선언된 타입이 아니라 객체의 실제 타입을 기반으로, 프로그램이 실제로 실행될 때 어떤 메서드를 실행할지 결정합니다.

상속 장에서 메서드 오버라이딩에 대해 배웠습니다. 다형성을 만드는 것은 부모 참조가 자식 객체를 가리킬 때 Java가 이를 처리하는 방식입니다:

public class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Bark!");
    }
}

public class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Meow!");
    }
}

이제 부모 타입 참조를 사용할 때 어떤 일이 일어나는지 살펴보겠습니다:

Animal myPet = new Dog();
myPet.speak();  // 출력: Bark!

myPet = new Cat();
myPet.speak();  // 출력: Meow!

myPetAnimal로 선언되었더라도, Java는 런타임 시 실제 객체 타입을 기반으로 오버라이딩된 메서드를 호출합니다. 이것을 동적 메서드 디스패치(dynamic method dispatch)라고 합니다. JVM은 변수가 무엇인지가 아니라 객체가 실제로 무엇인지를 확인합니다.

이를 통해 서로 다른 객체를 균일하게 처리하는 것과 같은 강력한 패턴이 가능해집니다:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // 각자 자신의 버전을 호출합니다
}

런타임 다형성을 사용하면 부모 타입을 다루면서도 자동으로 올바른 자식 클래스의 동작을 사용하는 유연한 코드를 작성할 수 있습니다.

challenge icon

챌린지

쉬움

런타임 다형성(run-time polymorphism)이 실제로 어떻게 작동하는지 보여주는 알림 시스템을 구축해 보겠습니다. 런타임 시 실제 객체 유형에 따라 동일한 메서드 호출이 서로 다른 출력을 생성하는 알림 유형 계층 구조를 만들 것입니다.

코드는 네 개의 파일로 구성됩니다:

  • Notification.java: 모든 알림이 공유하는 기본 클래스를 만듭니다. 모든 알림에는 recipient 필드(String)가 있습니다. 이를 초기화하는 생성자, getter 메서드, 그리고 "Sending notification to " + recipient를 출력하는 send() 메서드를 포함하세요.
  • EmailNotification.java: Notification을 상속받는 클래스를 만듭니다. 이메일 알림에는 subject 필드가 추가됩니다. 부모 클래스 초기화를 위해 super를 사용하세요. send() 메서드를 오버라이드하여 "Emailing " + getRecipient() + ": " + subject를 출력하도록 합니다.
  • SMSNotification.java: Notification을 상속받는 또 다른 클래스를 만듭니다. SMS 알림에는 phoneNumber 필드가 추가됩니다. send()를 오버라이드하여 "Texting " + phoneNumber + " for " + getRecipient()를 출력하도록 합니다.
  • Main.java: 여기서 런타임 다형성이 빛을 발합니다! 수신자 이름, 이메일 제목, 전화번호라는 세 가지 입력을 받게 됩니다. Notification 참조 배열을 생성하고 다음 순서대로 세 개의 객체를 담으세요: 기본 Notification, EmailNotification, SMSNotification (모두 동일한 수신자 사용). 그런 다음 배열을 루프하며 각 요소에 대해 send()를 호출하세요. Java가 각 객체의 실제 유형에 따라 올바른 오버라이드 버전을 자동으로 호출하는 것을 확인해 보세요!

수신자 이름(String), 이메일 제목(String), 전화번호(String)의 세 가지 입력을 받게 됩니다.

출력은 세 줄로 표시되어야 합니다. Notification 참조에 대해 동일한 메서드를 호출하더라도 각 send() 호출은 서로 다른 출력을 생성합니다. 이것이 바로 동적 메서드 디스패치(dynamic method dispatch)의 작동 원리입니다!

치트 시트

메서드 오버라이딩은 런타임 다형성(run-time polymorphism)을 보여줍니다. 여기서 Java는 변수의 선언된 타입이 아니라 객체의 실제 타입을 기반으로 프로그램이 실행될 때 어떤 메서드를 실행할지 결정합니다.

부모 참조 변수가 자식 객체를 가리킬 때, Java는 런타임 시 실제 객체 타입을 기반으로 오버라이딩된 메서드를 호출합니다:

public class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Bark!");
    }
}

Animal myPet = new Dog();
myPet.speak();  // Output: Bark!

myPet = new Cat();
myPet.speak();  // Output: Meow!

이것을 동적 메서드 디스패치(dynamic method dispatch)라고 합니다. JVM은 변수가 무엇이라고 말하는지가 아니라 객체가 실제로 무엇인지를 확인합니다.

런타임 다형성을 통해 서로 다른 객체들을 균일하게 처리할 수 있습니다:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // 각각 자신의 버전을 호출합니다
}

직접 해보기

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 입력 읽기
        String recipient = scanner.nextLine();
        String subject = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        
        // TODO: 3개의 요소를 가진 Notification 참조 배열 생성
        // 배열은 다음 순서대로 포함해야 합니다:
        // 1. 기본 Notification 객체
        // 2. EmailNotification 객체
        // 3. SMSNotification 객체
        // 모두 동일한 recipient를 사용
        
        // TODO: 배열을 순회하며 각 요소에 대해 send() 호출
        // 이것은 런타임 다형성(run-time polymorphism)을 보여줍니다!
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨