InvokeExact для объекта, тип которого динамически загружается загрузчиком классов
Я потратил целый день на эту проблему. Моя проблема заключается в том, как сделать метод 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. Затем MethodHandle myMh создается для функции добавленияAddSample
но вызов myMH терпит неудачу, потому что receiveType не совпадает.
myMH.invokeExact
вызывает ошибку во время выполнения
java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int
потому что получатель этогоmyMH
объявляется как expClass (AddSample), но получательobj
текущий предоставленный объявлен BaseTemaplte, хотя класс объекта является AddSample. 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)
, и это приводит к ошибке во время выполнения, которая мне была показана ранее.
Спасибо