问题
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
回答1:
You can’t use invokeExact
if the compile-time type of an argument doesn’t match the MethodHandle
’s parameter type. It doesn’t help to play around with the Generic constructs like invoking cast
on a Class<T>
, the dynamic type is still unknown to the compiler.
Or, in other words, due to type erasure, the type of tObj
is still Object
on the byte code level. And this is the “invoked type” of the MethodHandle
.
The simplest solution is to use invoke
rather than invokeExact
.
The only thing you can do if you want to use invokeExact
, is to transform the MethodHandle
to the type which you will eventually invoke, i.e. change the type of the first parameter to Object
:
myMH=myMH.asType(myMH.type().changeParameterType(0, Object.class));
// now it doesn’t matter that obj has compile-time type Object
assertEquals((int)myMH.invokeExact(obj, "addintaasdsa", 10, 20.0f), 12);
回答2:
To make sense, you'll have to type the method ( otherwise the cast is pointless):
public <T> void doSomething() {
BaseTemplate obj = ...
Class<T> newType = Class('AddSample');
T t = newType.cast(obj);
Without the method typing, you can't tie the dynamic class to the type to be cast to.
来源:https://stackoverflow.com/questions/28511836/invokeexact-on-the-object-whose-type-is-dynamically-loaded-by-classloader