きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

暇つぶしにjava.lang.invokeを使ってみた

ただのJava屋なので、まず使うことはないですが、試しに使ってみました。

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; import java.util.Arrays; public class Main { public static void main(String args) { Main main = new Main(); main.test("TestString"); } public void test(Object obj) { Lookup lookup = MethodHandles.lookup(); try { // 同じクラスを引数に取るコンストラクタを探す // 注意点は、Void.classじゃなくてVoid.TYPEが必要。 MethodHandle mh = lookup.findConstructor(obj.getClass(), MethodType.methodType(Void.class, obj.getClass())); Object retConstructor = mh.invoke(obj); // 確認 System.out.println(retConstructor); System.out.println(retConstructor.getClass()); // toCharArray()してみる。引数なし、戻り値あり(しかもプリミティブ配列) MethodHandle toCharArray = lookup.findVirtual(obj.getClass(), "toCharArray", MethodType.methodType(char.class)); Object retToCharArray = toCharArray.invoke(retConstructor); // 確認 System.out.println(Arrays.toString( (char[]) retToCharArray)); // concat(String)してみる。引数あり、戻り値あり。 MethodHandle concat = lookup.findVirtual(obj.getClass(), "concat", MethodType.methodType(obj.getClass(), obj.getClass())); Object retConcat = concat.invoke(retConstructor, retConstructor); // 確認 System.out.println(retConcat); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 

出力結果

TestString class java.lang.String [T, e, s, t, S, t, r, i, n, g] TestStringTestString

こんな感じです。

あ、しまった、invokeExactで書くべきだった。まあいいか。

ちなみに、誤ってVoid.classを使った場合はこういうのがでます。

java.lang.NoSuchMethodException: no such constructor: java.lang.String.<init>(String)Void at java.lang.invoke.MemberName.makeAccessException(MemberName.java:524) at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:654) at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1087) at java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:681) at Main.test(Main.java:19) at Main.main(Main.java:11) 

 おそらく、コンストラクタでなくてメソッドでも、同じような結果になるでしょう。

 

今回のコードは、ほぼ導入部分でしかない。

本来ならこのパッケージはもっと濃ゆい使い方をしないと意味がないと思う。

CallSiteとかSwitchPointとかのほうが重要な匂いがする。

 

あとinvoke/invokeExactを呼ぶとThrowableが飛んでくるので

リフレクションから置き換えて実験してみる意欲が一気に途絶えた。

WrongMethodTypeExceptionという非検査例外も飛んでくる。

そのためWrongMethodTypeExceptionを処理したい場合は、

かならず先にcatchしないといけない。

マルチキャッチも出来ないからね。

 

リフレクションよりは速いのだろうけど、

リフレクションと全く同じことが出来るわけでもない。

使う機会はまずないけど、Modifierはとりあえず取れなくなる。

MethodとConstructorをMethodHandle一本に統合できるけど、

統合した後に区別したくなったらどうすればいいのかな・・・。

 

まだまだ研究が必要そうだな。