본문 바로가기
프로그래밍

객체지향에서 다형성(polymorphism) 이란?

by 테크 가디언 2024. 5. 21.

다형성(polymorphism)은 객체지향 프로그래밍(OOP)의 핵심 개념 중 하나로, 동일한 인터페이스를 통해 서로 다른 데이터 타입을 처리할 수 있는 기능을 말합니다. 다형성을 통해 코드의 유연성과 확장성을 높일 수 있으며, 이는 주로 상속과 인터페이스를 통해 구현됩니다. 다형성에는 다음과 같은 두 가지 주요 형태가 있습니다.

 

 

  • 컴파일 시간 다형성 (정적 다형성)
  • 런타임 다형성 (동적 다형성)

 

1. 컴파일 시간 다형성 (정적 다형성)

컴파일 시간 다형성은 메서드 오버로딩(method overloading)과 연산자 오버로딩(operator overloading)을 통해 구현됩니다. 이는 컴파일 시점에 호출될 메서드가 결정되는 것을 의미합니다.

 

메서드 오버로딩

같은 이름의 메서드를 여러 개 정의하되, 매개변수의 타입이나 개수가 다른 경우를 말합니다. 자바를 예로 들어 설명하겠습니다.

class MathOperations {
    public int add(int a, int b) {
        return a + b;
    }
    
    public double add(double a, double b) {
        return a + b;
    }
    
    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

public class Main {
    public static void main(String[] args) {
        MathOperations math = new MathOperations();
        System.out.println(math.add(5, 3));       // 출력: 8
        System.out.println(math.add(5.5, 3.5));   // 출력: 9.0
        System.out.println(math.add(1, 2, 3));    // 출력: 6
    }
}

 

연산자 오버로딩

연산자 오버로딩은 C++ 같은 언어에서 제공되며, 특정 연산자를 사용자 정의 타입에 대해 다르게 동작하도록 정의할 수 있습니다.

#include <iostream>

class Complex {
public:
    int real, imag;
    Complex(int r, int i) : real(r), imag(i) {}

    // '+' 연산자 오버로딩
    Complex operator + (const Complex &obj) {
        Complex res(0, 0);
        res.real = real + obj.real;
        res.imag = imag + obj.imag;
        return res;
    }
};

int main() {
    Complex c1(3, 4), c2(1, 2);
    Complex c3 = c1 + c2;
    std::cout << c3.real << " + " << c3.imag << "i" << std::endl; // 출력: 4 + 6i
    return 0;
}

 

2. 런타임 다형성 (동적 다형성)

런타임 다형성은 주로 상속과 인터페이스를 통해 구현되며, 객체의 타입에 따라 동일한 메서드 호출이 다른 동작을 하는 것을 의미합니다. 이는 메서드 오버라이딩(method overriding)을 통해 이루어집니다.

 

메서드 오버라이딩

상위 클래스의 메서드를 하위 클래스가 재정의하여, 동일한 메서드 호출이 다른 동작을 하게 합니다.

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Dog barks");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Cat meows");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();
        
        myDog.makeSound();  // 출력: Dog barks
        myCat.makeSound();  // 출력: Cat meows
    }
}

 

인터페이스

인터페이스는 클래스가 특정 메서드를 구현하도록 강제하는 계약을 정의합니다. 다양한 클래스가 같은 인터페이스를 구현함으로써 다형성을 실현할 수 있습니다.

interface Shape {
    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Rectangle();
        
        shape1.draw();  // 출력: Drawing a circle
        shape2.draw();  // 출력: Drawing a rectangle
    }
}

 

다형성의 장점

 

  • 유연성: 코드의 수정 없이 새로운 클래스나 메서드를 추가할 수 있습니다.
  • 재사용성: 상위 클래스의 코드를 재사용하여 하위 클래스를 구현할 수 있습니다.
  • 유지보수성: 코드의 변경이 적은 범위에 국한되므로 유지보수가 용이합니다.
  • 인터페이스 사용의 장점: 코드의 구조와 가독성을 높이고, 의존성을 줄일 수 있습니다.

 

다형성은 객체지향 프로그래밍에서 코드의 재사용성과 유연성을 극대화하는 중요한 개념입니다. 이를 잘 활용하면 보다 효율적이고 유지보수하기 쉬운 소프트웨어를 개발할 수 있습니다.

 

 

반응형