図5 C++のオブジェクトと静的型の関係
図5 C++のオブジェクトと静的型の関係
[画像のクリックで拡大表示]

オブジェクト指向で開花した動的型

 プログラミング言語におけるデータ型は,FORTRANに起源がある変数や式に型を指定する静的型と,Lispを起源とし,データそのものに型情報を付加する動的型の2つの方法に分かれました。静的型はFORTRANからCOBOL,ALGOLを経由して多くのプログラミング言語に引き継がれました。

 Cはデータ型を持たないBCPLを祖先としますが,ALGOLなどの影響を受けて静的型を導入しています。

 一方の動的型は,長らくLispとその影響を受けた言語(例えばLOGO)でのみ採用されてきましたが,あることをきっかけに広く受け入れられるようになります。

 その「あること」とは,オブジェクト指向プログラミングです。最初のオブジェクト指向言語であるSimulaはALGOLの影響を強く受けた言語なので,整数などの基本的なデータ型については静的型を採用していました。しかし,新たに導入された「オブジェクト」については,どのクラスのオブジェクトであってもすべて「Ref」という型で表現しています。

 どのクラスのオブジェクトであっても静的な型としてはあくまでもRef型であり,区別はありません。Ref型のデータについては,どのようなオブジェクトであるかという情報をオブジェクト自身が知っているという,まさに動的型を採用していたのです。

 考えてみると,オブジェクトが自分自身の種別に関する情報を持っていることと,ある変数にいろいろな種別のデータが格納されることの2点は,オブジェクト指向の重要な要素であるポリモーフィズムに必要な条件です。なぜなら,変数の型がデータの型と完全に一致する静的型の下では,実行時の型によって適切な処理を選択することはあり得ないからです。Lispを起源とする動的型がなければオブジェクト指向は存在しなかったことでしょう。

 Simulaで発生したオブジェクト指向という概念を引き継いだSmalltalkはLispのように全面的に動的型を採用した言語です。SmalltalkとLispはみかけは全く違いますが,内部構造を見ると双子のようにそっくりです。一方,Lispの方もオブジェクト指向を取り込み,独自に発展させていきました。

 とにかく,1970年代から80年代にかけて,オブジェクト指向プログラミングは動的型の言語であるSmalltalkとLisp(の各種方言)によって支えられていたのです。

動的型と静的型の巡り合い

 1980年代,オブジェクト指向プログラミング言語の主流は動的型の言語にありました。しかし,21世紀の今日,最も広く使われている言語は静的型のオブジェクト指向言語であるJavaやC++でしょう。いったい,オブジェクト指向言語の歴史に何が起きたのでしょうか。

 1980年代前半,Simulaの影響を受けて1つのオブジェクト指向言語が誕生します。C++です。C++はCのすべての機能を含みながら,Simula由来のオブジェクト指向機能を追加しています*2

 Simulaは「オブジェクト以外は静的型」という言語でした。Simulaの影響を強く受けたC++はあるルールを導入することで,オブジェクトに対しても静的型を導入しました。

 そのルールとは「継承されたクラス(サブクラス)のオブジェクトを継承元のクラス(スーパークラス)のオブジェクトと見なすことを許す」というものです。具体的には,例えばString(文字列)というクラスがObject(オブジェクト)というクラスのサブクラスだったとすれば,Stringクラスに所属するオブジェクトはObjectクラスのオブジェクトと見なしても差し支えない,という意味です(図5[拡大表示])。

 このルールにより,変数や式の型がコンパイル時に分かるという静的型の利点と,実行時の型によって適切な処理へ進むポリモーフィズムが両立できるようになったのです。

 変数や式の型がコンパイル時に分かるため,型の不整合によるエラーを実行前に発見できます。これは大きなメリットです。さらに,型情報を使ってコンパイル時に大胆な最適化を行い,プログラムを高速化する余地も生まれました。

 このような良い性質を帯びているためか,C++や1990年代になってC++の影響を受けて生まれたJavaやC#などの静的型を持つオブジェクト指向言語が広く使われるようになったのです。

静的型のメリット

 現在では静的型を持つオブジェクト指向言語が広く使われるようになっています。未来の話に入る前に,ここで静的型,動的型それぞれのメリットについてまとめておきましょう。

 静的型の最大のメリットは,やはり型の不整合によってコンパイル時にバグが発見できることでしょう。もちろん,すべてのプログラム中の間違い(バグ)をコンパイル時に発見するのは不可能ですが,バグは型の不整合を伴うことが多いため,エラーを機械的に発見してくれる機能は,たとえ完全でなくても,とてもありがたいことです。

 一方,動的型の言語ではコンパイル時にせいぜい文法エラーしか見つけることはできません。

 プログラム中で型が明示的に指定されているということは,コンパイル時に利用できる情報が多いということです。この情報を利用してコンパイラはプログラムをより高速に実行できるよう工夫できます。

 型情報についてはコンパイラにとどまりません。人間がプログラムを読むときにも,「この引数の型は何か」という情報がプログラム読解の大きなヒントになります。統合開発環境(IDE)には,この情報を利用してメソッド名などの自動補完ができるものもあるようです。これも利用できる型情報があるために実現できています。

 最後に,変数や式それぞれが型を持つということは,変数などがどのような役割を持つのかあらかじめきちんと考えることにつながります。プログラム記述時に考えなければならないことは増えますが,単純に悪いこととは言えません。むしろ,良いプログラム,信頼性の高いプログラムを書くためには必要なことであるとも捉えられます。

 こうして見ると,静的型には良いところばかりのように思えますね。しかし,欠点あるいは課題と呼ぶべき点もいくつかあります。

 その一つが,型を指定しないとプログラムを書けないことです。もちろん,型の指定は静的型の特徴の一つです。しかし,型はあくまでも補助的な情報でプログラムの本質ではありません。本筋に集中したいときにいちいち型を指定するのは煩雑ですし,一部の型宣言はただ単にコンパイラを満足させるためでは,という気持ちになることもあります。結果的にソース・コードの分量が多くなり,本当に大事な部分が埋没してしまう可能性も否定できません。

 もう一つは柔軟性の問題です。静的型があること自体,「この変数にはこの型のオブジェクトが入る」という制限を課したことになります。このような制限が将来の変化の足かせとなる可能性があります。前回学んだ多重継承やインタフェースを使うと,入り組んだ継承関係が生まれます。このとき指定すべき型を適切に選択するのはそれなりに難しい課題でしょう。

 まとめると,静的型はプログラムを書く人間が型宣言という形で積極的に情報を与えることによって,コンパイラや将来そのプログラムを読む人間が楽をしようというアプローチであると言えます。

【訂正】
図5が別の図と入れ替わっておりました。お詫びして訂正いたします。(2007.07.24)