What's Ungeneric

概要:ここでは、greflectでジェネリック型を扱う場合に基本となる概念、 アンジェネリック(ungeneric)について解説します。 この手法によりジェネリックオブジェクトにその型引数の適用値を 封入することができます。 greflectでは封入された型引数の適用値を使用して、 ジェネリックオブジェクト間の代入可能性や実行可能性を検証します これにより型安全(type safety)なリフレクションを提供します。

greflectが使用するトリック、アンジェネリック(ungeneric)

greflectの動作は、あるオブジェクトの条件と密接に関連があります。 この概念は説明的な用語で呼ぶならば「型変数が解決可能」 「具象的に継承した」といったところでしょうか。 greflectでは簡単のため、アンジェネリック(ungeneric)と呼ぶ事にします。

さて、説明の都合上、アンジェネリックでないオブジェクトの例 から先に見てみましょう。

             new Map<String,Integer>()
             new GenericDao<Person>(connection)  
これらのnew文を実行したとき、それぞれ Mapクラスのインスタンスと GenericDaoクラスのインスタンスが作成されます。しかしJavaの ジェネリックでは型消去 (type erasure)というテクニックで 実現されており、 これらのオブジェクトはJavaJM上では単にMapやGenericDao クラスのインスタンスとしてnewされます。 MapやGenericDaoは型変数によらず共通のクラスなので、 型変数が今Stringとして解釈されているのかであるか (あるいはIntegerであるかPersonであるか)、といった情報は保持して いません。 また各インスタンスで保持しているわけでもありません。 つまり、何処にも型変数を今どう解釈しているかの情報は無いのです。 このように型変数を確定させる情報をもたないオブジェクトは アンジェネリックでない、ジェネリックオブジェクトと言います。

参考文献:

Javaジェネリックを使ったコンパイル時の動的処理

Wikipedia: C SharpとJavaの比較

ジェネリック: Java vs C# Java のジェネリックは単なる糖衣構文である

次はアンジェネリックであるオブジェクトの例を紹介します。 アンジェネリックでないオブジェクトのnew文をアンジェネリック オブジェクトを生成するよう修正するのは簡単です。次に アンジェネリックオブジェクトの例を見てみましょう。

             new Integer(23)
             new Map<String,Integer>(){}
             new GenericDao<Person>(connection){}  
Integerのようにもともと型変数がないクラスのインスタンスも アンジェネリックであるといいます。 後の2つは上の例の後ろに"{}"がつきました。 これで単純なnew 文の代わりに匿名クラスのインスタンスを生成する 式となっています。

ここで作成される匿名クラスを仮にそれぞれ Anon1, Anon2とすると、 上のnew文は

             new Anon1()
             new Anon2(connection) 
と同じ意味になります。 Anon1, Anon2には次のような継承関係になっています。
             Anon1 extends Map<String,Integer>
             Anon2 exterds GenericDao<Person>  
Anon1やAnon2は単にMapやGenericDaoを継承しているのではなく、 Map<String,Integer>やGenericDao<Person>を継承しています。 つまり、 MapやgenericDaoの型変数に適用される具体的な型が特定されており、 その情報はAnon1やAnon2のクラス定義の一部としてバイトコートに 保持されます。つまりこれらの無名クラスはそのインスタンスが どのように型変数を解釈しているのかの情報を保持しているわけです。

ここまでアンジェネリックのテクニックを使用して型変数に適用される 型をオブジェクトに封入する方法について説明してきました。 しかしアンジェネリックオブジェクトを作成したからといって Java VMがそれを解釈して適切に動作してくれるわけではありません。

Java標準リフレクションAPIはジェネリック型情報を参照しないため、 アンジェネリックオブジェクト決定した型以外のオブジェクトを 代入することを躊躇しません。つまり例えば、 a = new ArrayList(Integer) に a.add("String") をinvokeしてしまうことは簡単にできてしまいます。 当然その後のコードでおかしなことが発生する要因となるしょう。

でももし、ジェネリック型情報を取得し妥当性を検証しつつ リフレクションを実行するなら、こういった問題を回避して より安全にリフレクションを使用できると思いませんか? それがgreflectで行っていることです。


$Id: ungeneric.xml 134 2008-07-03 12:41:15Z yo-zi $