いろいろな型で使えるクラスを作れない

Javaでは,いろいろなクラスに通用する汎用的なクラスを作れない。特にC++でのプログラミング経験がある人は,このことをとても不便に感じるようだ。

図4●テンプレートの仕組み。
条件に合う型であれば,クラス型でも数値型でもテンプレート・クラスの機能を利用できる

 C++ではこれをテンプレート(パラメタライズド・タイプ)と呼ぶ機能で実現している(図4[拡大表示])。クラスや関数を作成する時に,特定のクラスを使うのではなく,クラスを入れる代わりのもの(テンプレート)を使って処理を記述しておく注1)。テンプレートで呼び出しているメソッドや演算子を持っていれば,どのような型のオブジェクトでもそれを利用できるのである(リスト4[拡大表示])。

ダウン・キャストは読みにくく,危険

 テンプレートが使えない言語では,プログラミングの際に常に型の制約を受けてしまう。Javaを例にとって説明しよう。先ほども登場したVectorクラスに,オブジェクトを格納する(リスト5[拡大表示])。

 まず,Stringクラスのオブジェクトを生成してVectorに追加する。そしてVectorの最初の要素を取得する。このとき,このメソッドの戻り値を(String)で明示的にキャストしている。このキャストがなければ,コンパイル時にエラーが出る。一度Vectorに格納されたオブジェクトは,Object型としか扱えなくなるからだ。

 このキャストをつけるのは二つの意味で不合理だ。Vectorに入れた要素には元々「型」の情報しか入っていないということがわかり切っている。それなのに,いちいちプログラマがキャストし直さなければならない。冗長な記述が要求されてコードが不必要に長くなる。

 それ以上に問題なのが,実はこの操作が危険をはらんでいる点にある。リスト5では,Vectorから取り出したオブジェクトをString型だと決めつけてキャストしている。これがいつも通用するとは限らない。仮にこのVectorに,外部からまったく違う型の(Stringを継承しているわけでもない)オブジェクトが追加されていたら,型が一致しないためにキャストができずエラーが発生する。

 このように,継承関係の上位にあるクラス(この場合はObject)をその派生クラス(この場合はString)に変換することをダウン・キャストという。ダウン・キャストにはこのような危険が伴うため,プログラミング上ではできるだけ避けるべきだと言われている。しかし,VectorのようなCollectionのクラスを利用する場合は避けられない。

 Javaのこの欠点は,近く解決されることになりそうだ。Javaの言語仕様改訂の権限を持つJCP(Java Community Process)が,C++のテンプレートに相当するGenericsを導入する方針なのだ。具体的な時期は明かではないが,Java2 SDK 1.5以降での導入が見込まれる注2 )。

リスト4●C++でのテンプレートのコードの一部。
Tという変数が型を総称するために使われている。値を2倍にしたいクラスがあれば,Tの部分にそのクラス名を指定してテンプレート・クラスを生成する。ただしこのテンプレートは,*演算子を持つクラスしか利用できない。その他のクラスが利用しようとすると,コンパイル・エラーとなる
 
リスト5●JavaでVectorに文字列オブジェクトのデータを格納するコード。
値を取り出す時に,必ずキャストする必要がある

(大森 敏行、八木 玲子)