私はこの問題に一日を費やしました。私の問題は、クラス型がプログラムの実行時に動的にロードされるインスタンスで MethodHandle.invokeExact 呼び出しを行う方法です。問題をより明確にするために、サンプルコードを以下に示します。
Class<?> expClass = new MyClassLoader().load(....)
//expClass is AddSample.class which is subclass of BaseTemplate
BaseTemplate obj = expClass.getConstructor(...)
.newInstance(...);
MethodHandle myMH = MethodHandles.lookup().findVirtual(expClass, methodName,..);
System.out.println("Object type "+obj.getClass()); //Print AddSample
// If obj is declared as "AddSample obj", the runtime would be OK.
assertEquals((int)myMH.invokeExact(obj,"addintaasdsa" , 10 , 20.0f), 12);
このサンプルでは、expClass が動的にロードされ、そのクラス タイプはAddSample
です。次の行の obj インスタンスは BaseTemplate であると宣言されており、その実際の型はAddSample
. クラス AddSample は BaseTemplate のサブクラスです。次に、メソッドハンドル myMh を add 関数に作成しAddSample
ますが、receiverType が一致しないため、myMH の呼び出しは失敗します。
はmyMH.invokeExact
実行時エラーを発生させます
java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int
this のレシーバーはmyMH
expClass (AddSample) にあると宣言されてobj
いますが、obj のクラスは AddSample ですが、現在提供されているレシーバーは BaseTemaplte と宣言されているためです。InvokeExact では、パラメーターが正確に一致する必要があります。
私の問題は次のように単純化される可能性があります:インスタンスをその基本型から動的にロードされる子型にキャストする方法は?
BaseTemplate obj = ...
Class<?> newType = Class('AddSample') //dynamic loaded...
obj の宣言された型を、動的にロードされる AddSample に変更します..?
UPDATE:
Class<T> expClass = (Class<T>) new MyClassLoader().run(className, methodName, b);
BaseTemplate obj = ..
Class<T> newType = (Class<T>) obj.getClass().getClassLoader().loadClass("AddSample");
T tObj = newType.cast(obj);
assertEquals((int)myMH.invokeExact(tObj,"addintaasdsa" , 10 , 20.0f), 12);
キャストを使用しても、以前の結果と同じ問題を解決するのには役立ちません。その理由は、指定されたパラメーターが myMH 宣言と完全に一致していないためです。生成されたバイトコードを確認すると、より明確になります。
L23 # For cast
LINENUMBER 126 L23
ALOAD 10: newType
ALOAD 8: obj
INVOKEVIRTUAL Class.cast (Object) : Object #tObj is Object and its real type is AddSample here
ASTORE 11
L24
LINENUMBER 128 L24
ALOAD 9: myMH # Push myMH to stack
ALOAD 11: tObj # Push tObj to Stack. tObj is declared Object type and its real type is AddSample.
LDC "addintaasdsa" #Push String to Stack
BIPUSH 10 #Push int to Stacl
LDC 20.0 #Push float to Stack
INVOKEVIRTUAL MethodHandle.invokeExact (Object, String, int, float) : int
myMH は を指して
(AddSample,String,int,float)int
いますが、パラメーター:
(Object, String, int, float)
を指定すると、前に示した実行時エラーが発生します。
ありがとう