ポリモーフィズムとは
ポリモーフィズム(polymorphism)とは、プログラミングにおいて、異なる型に対して一貫したインターフェースを提供する概念です。これは、同じ操作が異なるデータ型に対して異なる動作をするように見せることを可能にします。日本語では「多態性」や「多相性」と訳されることもあります。ポリモーフィズムは、コードの柔軟性、再利用性、拡張性を高めるために不可欠な要素です。
ポリモーフィズムの種類
ポリモーフィズムは、主に以下の3つの種類に分類されます。
1.
アドホック多相(ad hoc polymorphism)
特定の型の集合に対して、同じ名前の関数や演算子を異なる動作で適用できるようにする多相性です。これは、関数オーバーロードや、型クラスなどが該当します。
例えば、同じ`add`関数が、整数同士の足し算、浮動小数点数同士の足し算、文字列の連結など、引数の型に応じて異なる処理を行うことができます。
2.
パラメトリック多相(parametric polymorphism)
型を抽象化し、特定の型に依存しない汎用的なコードを記述できるようにする多相性です。ジェネリクスや、関数型言語の型構築子などが該当します。
例えば、`List
`のようなジェネリックなリスト型は、`T`がどんな型であっても同じように扱えます。また、`map`関数のような高階関数は、渡された関数を任意の型のリストに対して適用できます。
3. サブタイピング(subtyping)
ある型が別の型のサブタイプ(下位型)であるとき、そのサブタイプのオブジェクトを、上位型を期待する場所で使えるようにする多相性です。オブジェクト指向プログラミングにおける継承と動的ディスパッチが代表例です。
例えば、`Animal`という基底クラスがあり、`Dog`や`Cat`がその派生クラスである場合、`Animal`型の引数を取る関数に、`Dog`や`Cat`のオブジェクトを渡すことができます。
その他に、ロー多相(row polymorphism)やポリタイピズム(polytypism)といった概念も存在します。
ポリモーフィズムの歴史
ポリモーフィズムの概念は、1960年代から研究が進められてきました。
1967年、クリストファー・ストレイチーが論文でアドホック多相とパラメトリック多相の概念を初めて提唱しました。
1985年、ピーター・ウェグナーとルカ・カーデリが、Simula67の継承を説明するためのインクルージョン多相(サブタイピング)の概念を提唱しました。
これらの3つの概念が組み合わさり、今日のポリモーフィズムの概念を形成しています。
また、ロー多相は1989年に、ポリタイピズムは同時期にジェネリックプログラミングの概念と共に提唱されました。
各種ポリモーフィズムの詳細
アドホック多相
アドホック多相は、関数や演算子が、異なる型に対して異なる振る舞いをすることを可能にする多相性です。関数オーバーロードはアドホック多相の典型的な例です。
cpp
// C++の例
int Add(int a, int b) { return a + b; }
double Add(double a, double b) { return a + b; }
動的型付け言語では、アドホック多相はより複雑になります。実行時に型が決定するため、適切な関数が実行時まで特定できない場合があります。型強制多相もアドホック多相の一形態とみなされることがあります。
パラメトリック多相
パラメトリック多相は、型に依存しない汎用的なコードを記述するための多相性です。ジェネリクス(総称型)はこの多相性の代表的な例です。
haskell
length :: [a] -> Int -- 任意の型のリストの長さを返す関数
map :: (a -> b) -> [a] -> [b] -- リストの要素に関数を適用する関数
パラメトリック多相は、関数型プログラミングで頻繁に使用され、コードの再利用性と抽象度を高める上で重要な役割を果たします。
サブタイピング
サブタイピングは、ある型が別の型のサブタイプである場合に、それらを交換可能に扱うための多相性です。オブジェクト指向プログラミングでは、継承と仮想関数がサブタイピングを実現するために使われます。
java
// Javaの例
class Animal { public void letsHear() { System.out.println("Generic Animal Sound"); } }
class Cat extends Animal { @Override public void letsHear() { System.out.println("Meow!"); } }
class Dog extends Animal { @Override public void letsHear() { System.out.println("Woof!"); } }
public static void main(String[] args) {
Animal animal1 = new Cat();
Animal animal2 = new Dog();
animal1.letsHear(); // Meow!
animal2.letsHear(); // Woof!
}
オブジェクト指向言語では、仮想関数テーブル(vtable)を使用して、メソッド呼び出しを適切な実装にディスパッチします。多くの言語では単一ディスパッチを採用していますが、多重ディスパッチをサポートする言語も存在します。
ロー多相
ロー多相は、レコード型の構造的な可変長要素を扱うための多相性です。動的型付けやダックタイピングを説明するための概念として使われます。
ポリタイピズム
ポリタイピズムは、パラメトリック多相の亜流と見なすことができ、型がコンテナを脱着する概念を扱います。ジェネリックプログラミングを説明するために使われることがあります。
実装的な側面
ポリモーフィズムは、実装が選択されるタイミングによって、静的(コンパイル時)と動的(実行時)に区別できます。静的ポリモーフィズムはコンパイル時に実装が決定するため高速に実行できますが、柔軟性に欠けます。動的ポリモーフィズムは実行時に実装が決定するため柔軟性がありますが、速度は遅くなります。通常、アドホック多相とパラメトリック多相は静的に解決され、サブタイピングは動的に解決されます。
モノモーフィズムとの対比
ポリモーフィズムの対義語として、モノモーフィズム(単態性)があります。モノモーフィックな型システムでは、サブルーチンは特定の型に対してのみ動作するように定義されています。
ポリモーフィズムを使用すると、汎用的なインターフェースを定義し、異なる型に対して同じ操作を適用することができます。これにより、コードの再利用性と抽象度が向上し、より柔軟なシステムを構築することが可能になります。
まとめ
ポリモーフィズムは、プログラミングにおける重要な概念であり、コードの柔軟性、再利用性、拡張性を高める上で不可欠です。アドホック多相、パラメトリック多相、サブタイピングといった多種多様な多相性の概念を理解し、適切に利用することで、より高品質なソフトウェア開発が可能になります。
関連項目
型システム
総称型
多重定義
オーバーライド
仮想関数テーブル
多重ディスパッチ
ダック・タイピング
* 継承