データ型とポリモーフィズム
ポリモーフィズム
図形エディターのように、何種類かの図形を表示するアプリケーションを考えてみましょう。マウスで選択した図形オブジェクトが変数objに入れられていて、その図形の色を調べたいとします。色を調べるためのメソッドが図形のクラスごとに異なっていたとすると、図3-1のように図形の種類ごとに異なるメソッド呼び出しを行わなければならないかもしれません。しかし、色を調べるメソッド名がcolorだと決まっていたとすれば、図形の種類によらず、図3-2のように呼び出すことができれば非常に簡潔で分かりやすいのではないでしょうか。
C++やJavaでは、先に説明した仮想メソッドの機能を使ってこれを実現できます。そのためにはまず、共通のスーパークラス(例えばFigure)を作り、そのクラスでcolorというメソッドを仮想メソッドとして定義します。ほかの図形クラスはそのクラスのサブクラスとして定義し、それぞれの具体的なメソッドで上書きします。このようにすると、図3-2において変数objに代入されているインスタンスの種類に応じて、実行されるメソッドが動的に決定できます(図3-3(a))。
一方、型の概念がないSmalltalkのような言語では、クラスの継承関係のないオブジェクトであっても、colorというメソッドを処理可能でさえあれば図3-2に相当する記述を使って実行させることができます(図3-3(b))。
このように、対象となるインスタンスオブジェクトの種類に応じて、実行されるメソッドが変化する仕組みをポリモーフィズムと呼びます。日本語では型の多態性、あるいは多相性などと呼ばれることがあります。また、Smalltalkの場合にはポリモーフィズムに関して型の制限がありませんが、C++やJavaでは継承関係があるクラスの間でのみこのような扱いが可能です。これを、型階層に基づくポリモーフィズムと呼ぶことがあります。
Objective-Cという言語はC言語にSmalltalk的なオブジェクト指向の機能を取り入れた言語ですが、この言語では Smalltalkのようにまったく型に関係なくメソッドを実行することもできますし、型階層に基づいた記述を行うこともできます。
Smalltalkのように型に基づかないで実行を行う言語では、実際に実行してみないとその変数にどんなクラスのインスタンスが代入されているかが分かりません。メソッドが正しく処理できず、実行時エラーとなることもあります。一方、型階層に基づいていればコンパイル時に型のチェックが行えますので、少なくともメソッドが実行できないということは起こりません。
抽象クラス
図3-3(a)の例の説明で、具体的な図形クラスのスーパークラスにあたるFigureのようなクラスを定義し、ほかの図形クラスをそのサブクラスにするという説明をしました。この場合のFigureのようなクラスは、サブクラスが共通して備えるべきインスタンス変数やメソッドを宣言して、サブクラスの基本的なインタフェースを決定しますが、それ自体のインスタンスオブジェクトを生成することはありません。
このような役割を担うクラスを抽象クラスと呼びます。
また、例えばクラスFigureではサブクラスで共通して使われるメソッドであるcolorを定義します。このメソッドの実装がどのサブクラスでも同じであるという場合にはクラスFigureでcolorを定義し、サブクラスではそれを利用すれば良いでしょう。一方、サブクラスごとにメソッドの実装が異なる場合にはクラスFigureでcolorに具体的な実装を用意する必要はありません。しかし、サブクラスのインタフェースを共通にするためにはスーパークラスでメソッドの宣言だけはしておく必要があります。
このような目的で宣言されるメソッドを抽象メソッドと呼びます。抽象メソッドは実行できませんので、抽象メソッドを持つクラスは抽象クラスになります。抽象メソッドであることを明示的に宣言できる言語もありますが、内容のない(あるいはデフォルトの)メソッド定義を行うことでそれに代える言語もあります。C++の場合は宣言だけのメンバ関数であることを示す構文を持ち、純粋仮想関数と呼んでいます。
なお、本稿の執筆にあたって、以下を参考にしました。
青木淳、浅岡浩子、澤本依里『Smalltalkで学ぶオブジェクト指向プログラミングの本質』日経BP社(発行年:2008)
林晴比古『改訂新Java言語入門シニア編』ソフトバンククリエイティブ(発行年:2005)
柏原正三『C++効率的最速学習徹底入門』技術評論社(発行年:2002)