I have spend whole day on this problem. My problem is how to make an MethodHandle.invokeExact invocation on an instance, whose class type is dynamically loaded at program runtime. To make problem more clear, i show my sample code below:
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);
In this sample, expClass is dynamically loaded and its class type is AddSample
. The obj instance in the next line is declared to be BaseTemplate, and its real type is AddSample
. Class AddSample is subclass of BaseTemplate. Then a MethodHandle myMh is created to the add function of AddSample
but the invocation of myMH fails because of receiverType does not match.
The myMH.invokeExact
raises runtime error
java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int
because the receiver of this myMH
is declared to be on expClass (AddSample), but the receiver obj
current provided is declared BaseTemaplte, though the obj's Class is AddSample. InvokeExact requires exact parameter match.
My problem might be simplified as: how to cast an instance from its base type to a child type which is dynamically loaded?
BaseTemplate obj = ...
Class<?> newType = Class('AddSample') //dynamic loaded...
change obj's declared type to AddSample which is dynamically loaded..?
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);
Using cast does not help fix the problem which is the same previous result. The reason is still that the given parameter does not exact match myMH declaration. It would be more clear when i check the generated bytecodes:
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
the myMH points to
(AddSample,String,int,float)int
, but given parameters:
(Object, String, int, float)
,
and this result in runtime error which I was shown previously.
Thanks