概要:ここでは、greflectでのインスタンスメソッド呼び出しを説明します。
まず、型を指定してメソッドを実行する方法について 説明します。
1 // preparing
2 Reflection api = new Reflection();
3 Map<Integer,String> map = new HashMap<Integer,String>();
4
5 // put map value
6 api.new Invoker<String>(new Param<Map<Integer,String>>(map){}, "put",
7 new Param<Integer>(1234){}, new Param<String>("hoge"){})
8 {}.invoke();
9
10 // get map value
11 String r = api.new Invoker<String>(
12 new Param<Map<Integer,String>>(map){}, "get",
13 new Param<Integer>(1234){})
14 {}.invoke();
15 assertEquals("hoge", r);
2行目:リフレクションAPIオブジェクトを取得します。
これはリフレクションに必要なリソースを確保するためのオブジェクトで、
リフレクションの使用が終わるまで作成しなおす必要はありません。
3行目:リフレクション対象のマップを用意しています。
Java のジェネリクス実装はイレイジャーを使用しているため、
(参考文献1)ここで作成しているオブジェクトは、
単なるHashMapクラスのインスタンスです。
6〜8行目:リフレクションを使用して、2行目で作成したマップの
Map.set()メソッドを呼び出しています。
Invokerの第一引数、
new Param<Map<Integer,String>>(map){}
の部分がメソッドを呼び出すオブジェクトを指定しています。
ここで、パラメータに要求するジェネリック型が
Map<Integer,String>
であることを明示して、インスタンスを渡しています。
コンストラクタに渡すパラメータの型はParamの型引数のため、
Map<Integer,String>
に代入できる型の値を指定する必要があります。
最後に"{}"がある点も見逃さないでください。
通常のnew文ではなく無名クラスのnew文を使用しています。
こうすることでオブジェクトのジェネリック型をgreflectに教えることが
できるようになっています。
このテクニックをgreflectではアンジェネリック(ungeneric)と呼んでいます。
greflectではこのテクニックを多用します。
(参考文献2)。
続いてInvokerの第2引数はメソッド名、
第3引数以後はメソッドのパラメータの指定です。
パラメータの指定にはインスタンスの指定と同様、
Paramクラスのアンジェネリックオブジェクトで指定しています。
Invokerの型引数にStringを指定指定していますが、
これはメソッドの戻り値に合わせた型を指定する必要があります。
Map.put()の戻り値はvoidではなく
Map<K,V>の型引数のVであるので、
Map<Integer,String>でのVの解釈である
Stringを指定しています。
Invoker自身にもアンジェネリックのテクニックが使用されている点にも
注意してください。
最後のinvoke()が実際にMap.set()メソッドを実行します。
11〜14行目:今度はMap.get()で
マップの値を取得しています。
Invokerの型引数にStringを指定しているため、
キャスト無しで戻り値をString変数に代入しています。
最後のinvoke()が実際にMap.set()メソッドを実行します。
このメソッドの戻り値の型がInvokerの型変数のため、
戻り値の型はStringになります。
このためキャスト無しで直接String型の変数に代入できます。
15行目は取得値が"hoge"であることを確認する、JTestのアサート文です。
どうですか?Java標準リフレクションを使うより、簡潔でしょう?
参考文献:
[1]
ジェネリック: Java vs C#
/ Java のジェネリックは単なる糖衣構文である
[2]
What's Ungeneric
上の型を指定してメソッドを実行する方法では、 あらかじめオブジェクトのジェネリック型が分かっていないと 記述できません。 つまりは(無理やりキャストするのでないのなら) そこに代入する値のジェネリック型も確定していなければなりません。 しかしリフレクションを使用する環境では、 そもそもインスタンスの型が分かっていない状態でメソッドを 実行したいということもあります。 greflectではアンジェネリック(ungeneric)オブジェクトであれば (参考文献1)、 型を指定せずにインスタンスやパラメータに指定することができます。
次に型を指定せずにメソッドを実行する方法を紹介します。
1 // preparing
2 Reflection api = new Reflection();
3 Object map = new HashMap<Integer,String>(){};
4
5 // put map value
6 api.new Invoker<String>(new UngenericParam(map), "put",
7 new UngenericParam(1234), new UngenericParam("hoge"))
8 {}.invoke();
9
10 // get map value
11 String r = api.new Invoker<String>(new UngenericParam(map), "get",
12 new UngenericParam(1234))
13 {}.invoke();
14 assertEquals("hoge", r); 3行目:ハッシュマップを生成していますが、ここでアンジェネリックの テクニックを使用している点に注意してください(参考文献1)。 生成されるオブジェクトはHashMapを継承する、 ジェネリック型情報をもつ無名クラスのインスタンスになります。 これをObject型の変数に代入しています。 これでmap変数自体はジェネリック型情報を持たないようになりました。
6〜13行目:型を指定する場合のnew Param<型>(値){}
の代わりに、new UngenericParam(値)
という記述をします。
こちらの記述はアンジェネリックのテクニックは使用しません。
上の例ではインスタンス指定とパラメータ指定両方を 型を指定しないようにしていますが、もちろん型を指定する方法と 混在させて使用することもできます。
参考文献:
[1]
What's Ungeneric
概要:ここでは、greflectでのstaticメソッド呼び出しの コーディングを説明します。
ジェネリックでない型については、インスタンスを指定しない以外 実行方法は同じです。
1 // preparing
2 Reflection api = new Reflection();
3 // :
4 long now1 = System.currentTimeMillis();
5 long now2 = api.new Invoker<Long>(new Param<System>(){},
6 "currentTimeMillis")
7 {}.invoke();
8 long now3 = System.currentTimeMillis();
9
10 assertTrue(now1 <= now2);
11 assertTrue(now2 <= now3); 5行目:インスタンスは省略して メソッドを宣言している型を指定しています。
ジェネリック型については、型変数を指定せずにクラスだけ指定します。
1 enum MyState {
2 SOU, UTSU, FUTSUU
3 };
4 // preparing
5 Reflection api = new Reflection();
6 MyState s = MyState.SOU;
7 // :
8 s = api.new Invoker<MyState>(new Param<Enum>(){}, "valueOf",
9 new Param<Class<MyState>>(MyState.class){}, new Param<String>(
10 "UTSU")
11 {})
12 {}.invoke();
13
14 assertEquals(MyState.UTSU, s); 8行目:Enum<MyState>のように型変数を記述するのではなく、 Enumだけを指定しています。
概要:ここでは、greflectでのアンジェネリックオブジェクトの生成を説明します。
リフレクションAPIを使うような状況では、外部の処理から パラメータで渡された型についてインスタンスを作成するといった 処理を行うこともあります。 greflectで扱う場合にはこのときジェネリック型情報を オブジェクトに封入して生成できると便利です。
ここでは型をパラメータで渡してアンジェネリックインスタンスを 生成する方法を紹介します。
1 // preparing
2 Reflection api = new Reflection();
3 Param<?> instype = new Param<ArrayList<String>>(){};
4
5 // create ungeneric list
6 Object ins = api.new Instantiator<Object>(instype){}.instantiate();
7
8 // get map value
9 List<String> list = api.new Caster<List<String>>(ins){}.cast();
10 // use list
11 for(String s : list){
12 assertTrue(false);
13 }
14 assertTrue(true);
3行目:生成するインスタンスの型情報をParamオブジェクトとして
作成しています。
Param<?>型の変数に代入しているので、
ジェネリック型情報は失われています。
6行目:オブジェクトを作成しています。
Instantiatorの第1引数に生成するインスタンスの型を指定しています。
第2パラメータ以降にコンストラクタのパラメータを指定できますが、
ここではパラメータ無しのコンストラクタを使用しているので
指定していません。
Instantiatorの型変数には生成するオブジェクトの型を指定しています。
ここではObject型の変数に代入するので、Objectを指定しています。
もしObject型以外の、もっと具体的な型のオブジェクトを生成することが
分かっている場合には、その型を指定できます。
こうすることでその型の変数に代入することができるようにまります。
9行目:greflectではアンジェネリックオブジェクトを 指定した型にキャストする方法も用意されています。 これは単なるキャストとは違い、ジェネリック型も含めた型の安全性を 保障しつつその型への代入を行います。