Post

Polymorphism

부정확한 정보가 있을 수도 있습니다.

Polymorphism?

다형성(Polymorphism)이란 하나의 symbol(객체, 함수, 메소드, 변수, 상수 등)이 여러 개의 type을 가질 수 있도록 허용하는 언어상의 특성이다.

이를 크게 4가지 종류로 구분할 수 있다.


1. Ad-hoc polymorphism


Ad-hoc polymorphism이란 하나의 함수가 다양한 종류의 parameter에 대해 동작할 수 있는 특성이다. 단, 함수는 서로 다른 parameter 종류에 대해 다르게 동작하며, 이러한 동작을 일일이 추가해 주어야 한다. 따라서 Ad-hoc이라 불리는 것이다.

1
2
3
4
5
6
7
int add(int a, int b) {
    return a + b;
}

string add(string a, string b) {
    return a.append(b);
}
  • C++의 function overloading

함수 add는 아래의 2가지 타입이 될 수 있다.

  1. int, int -> int
  2. string, string -> string

따라서 add는 polymorphic function이다.


2. Parametric polymorphism


Parametric polymorphism은 하나의 함수가 generic한 data type을 지닌 parameter에 대해 동작할 수 있는 특성이다. 이 경우, 함수는 parameter의 실제 type과 관계없이 동일하게 동작한다.

1
2
3
4
template <typename T>
T add(T a, T b) {
    return a + b;
}
  • C++에서의 function template

파라미터 ab는 int, float, string 등 다양한 type이 될 수 있다. 함수 add도 polymorphic한 parameter에 따라 여러 type을 가진다. 따라서 add는 (parametrically) polymorphic function이다.


3. Subtype polymorphism


임의의 type A와 B가 있다고 하자. 또한 A type의 데이터를 B type의 데이터로 치환시켜도 어느 상황에서도 어떤 식으로든 동작한다고 하자. 이 경우 B는 A의 subtype이라고 칭하며, B type의 데이터는 A type에도 함께 속한 것으로 간주할 수 있다.

Subtype polymorphism이란 이와 같이 subtyping을 구현해 데이터가 여러 개의 type을 지닐 수 있게끔 한 특성이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
public:
    virtual void makeSound() {
        cout << "The animal makes a sound" << endl;
    }
};

class Dog : public Animal {
public:
    void makeSound() override {
        cout << "The dog barks" << endl;
    }
};

int main() {
    Dog dog;
    Animal* animal = &dog;
    animal->makeSound();
    return 0;
}
  • C++에서의 상속

Dog class는 Animal class로부터 상속받았으므로 Animal class의 인터페이스를 그대로 가진다. 따라서 Animal class의 객체는 모두 Dog class의 객체로 대체될 수 있으므로 Dog type은 Animal type의 subtype이다.

나아가 Dog class에 속한 객체 dog는 Dog type과 Animal type 둘 다 가진 것으로 간주되므로 polymorphic하다. 그렇기에 Animal type 포인터가 dog를 가리키고 있을 수 있는 것이다.


4. Coercion polymorphism


Coercion polymorphism은 특정 type과 대응하는 type conversion function을 두어 하나의 데이터가 여러개의 type으로 변환될 수 있게끔 한다. 이 역시 subtype 관계가 형성되므로 subtype polymorphism의 일종으로 볼 수 있겠지만 일반적으로 상속을 통한 subtyping만을 subtype polymorphism으로 부르는 듯 하다.

1
2
3
4
5
6
7
8
9
10
11
12
class A {
public:
    operator int() {
        return 1;
    }
};

int main() {
    A a;
    int i = a;
    return 0;
}
  • C++에서의 user-defined type conversion

class A의 인스턴스인 a는 사용자가 정의한 type conversion method으로 인해 int type으로 변환될 수 있다. 따라서 a는 A type과 int type을 지닌 것으로 간주되므로 polymorphic 하다.


보너스: subtyping vs inheritance


type은 객체의 interface에 집중하고, subtyping은 다형성을 통해 클래스 외부의 코드 재사용성을 높여준다.
class는 객체의 implementation에 집중하고, inheritance는 구현의 공유를 통해 클래스 내부의 코드 재사용성을 높여준다.

  • A가 B의 subtype이라 해도, A가 B를 상속하지 않을 수도 있다.
    파이썬의 duck typing과 C++에서의 user-defined type conversion이 그 예이다.
    또한 굳이 클래스가 아니더라도 primitive 데이터타입들 간에 subtype 관계가 있을 수 있다. Julia는 이를 명시적으로 보여주는 언어이다.

  • A가 B를 상속한다 해도 A가 B의 subtype이 아닐 수도 있다.
    A가 B의 메소드 전체를 상속받지 않거나 일부 메소드의 parameter 혹은 return type를 다형성이 깨지도록 override하면 subtype 관계가 아니게 된다.
    예를 들어, C++의 private 상속은 superclass의 공개 메소드를 숨기므로 subtyping이 일어나지 않는다. Eiffel이나 Dart의 경우에는 메소드 override 시 parameter를 더 ‘자세한’ type으로 변경이 가능하게끔 한다. 따라서 subtype 관계가 형성되지 않고, 만약 그래도 다형성을 보장한다면 type safe하지 않게 된다.

따라서 둘은 서로 독립적인 개념이다.


참고자료

https://en.wikipedia.org/wiki/Polymorphism_(computer_science)
https://en.wikipedia.org/wiki/Subtyping
https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)
https://www.cs.princeton.edu/courses/archive/fall98/cs441/mainus/node12.html

This post is licensed under CC BY 4.0 by the author.