Javaのgenericsはある型Xの型引数へより具体的な型を代入して得られる全ての部分型Yを実行時に同一視する。つまり、全てのYのための実行時型がXになるのだ。このような実現方法をとると、実行時に各Yを区別できないという欠点はあるが、genericsを導入する前と互換性を保てるという利点がある。これがJavaのgenericsの妥協点である。この妥協点のおかげで、型引数に代入された型の情報が得られないため、リフレクション使いとしては歯がゆい思いをすることになる。

このように情報が欠落するのは、実は型引数の情報だけでない。

Javaはgenericsを導入するに当たって、covariantな戻り値型を含む型の型引数の具体化を認めた。具体的には、次の型Xを派生させて型引数をより具体化した型Yを宣言することを認めたのだ。XのgetValue()の戻り値型はAの部分型でなければならない。そして、YがXから継承したgetValue()の戻り値型は具体化されBの部分型でなければならなくなっている。YのgetValue()は確かにXのgetValue()を置き換えることができるので継承関係と矛盾しない。

interface A {}
interface B extends A {}
interface X<_T_ extends A> {_T_ getValue();}
interface Y<_T_ extends B> extends X<_T_> {}

ちなみに、世の中には型引数を具体化すると置き換え関係が逆転する種類の型が存在する。型引数がメソッドの引数に使われている場合がそうである。そういう種類の型は型引数の具体化と継承関係が矛盾するため宣言できない。型引数を抽象化することで宣言できるようになるが、一般化して嬉しいことは少ない。さらに、型引数を具体化も抽象化もできない種類の型も存在する。

難しい豆知識は終わりにしよう。本題に戻ろう。
covariantな戻り値型を含む型の型引数の具体化を認めると言うことは、メソッドの戻り値型がサブクラスで具体化されることを意味する。java1.5はそういう型システムをコンパイル時に導入するついでに、メソッドをオーバーライドする際に戻り値型を具体化することも認めたのだ。
次の型XのgetValue()の戻り値型は型Aだが、型YのgetValue()の戻り値型は型Bになっている。

interface A {void foo();}
interface B extends A {void goo();}
abstract class X {abstract A getValue();}
abstract class Y extends X {abstract B getValue();}

このように戻り値型を具体化できるといいことがある。Xの中ではgetValue().foo()しか許されないが、Yの中ではgetValue().foo()とgetValue().goo()が許される。このテクニックを使うと、戻り値をキャストする必要がなくなるため安全性が高まる。Java1.5より前では、次のようにすることで、このテクニックを擬似的に表現できた。

interface A {void foo();}
interface B extends A {void goo();}
abstract class X {abstract A getValue();}
abstract class Y extends X {A getValue() {return getValueAsB();} abstract B getValueAsB();}

戻り値型だけを特殊化したメソッドgetValueAsB()を導入し、それを使って、Xから継承したgetValue()をオーバーライドするのだ。こうすることで、Yの中ではgetValueAsB().foo()とgetValueAsB().goo()が許される。

このように擬似的に表現することは、普通に考えれば意味には何の違いもない。しかし、メタプログラミングのことを考えると意味が違ってくる。前者ではYのgetValue()の戻り値型はBだが、後者ではAだ。つまり、後者ではオーバーライドされたメソッドからは具体化された戻り値型が得られない。このような事情から、後者では具体化された戻り値型の情報を使ったメタプログラムが作れないという問題がある。

しかし、Java1.5からは前者が使えるから作れるのでは?と思う人がいるかもしれないが、そうは問屋がおろさない。Java1.5では、前者のYのgetValue()の戻り値型は「あえて」Aになるにように設計されている。これも過去との互換性のためだ。あきらめなければならないのか・・・。

いろいろやってみたけど。結論、無理。

カテゴリー: 技術情報

1件のコメント

squld · 2008-02-03 22:48

> いろいろやってみたけど。結論、無理。

結局無理ですか~!?w

コメントを残す

メールアドレスが公開されることはありません。