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), и это приводит к ошибке во время выполнения, которая мне была показана ранее.

Спасибо

2 ответа

Решение

Вы не можете использовать invokeExact если тип аргумента во время компиляции не соответствует MethodHandleТип параметра. Это не помогает поиграться с общими конструкциями, такими как вызов cast на Class<T>динамический тип все еще неизвестен компилятору.

Или, другими словами, из-за стирания типа, типа tObj все еще Object на уровне байтового кода. И это "вызываемый тип" MethodHandle,

Самое простое решение - использовать invoke скорее, чем invokeExact,

Единственное, что вы можете сделать, если хотите использовать invokeExact, чтобы преобразовать MethodHandle к типу, который вы в конечном итоге будете вызывать, т.е. измените тип первого параметра на 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);

Чтобы иметь смысл, вам нужно набрать метод (иначе приведение будет бессмысленным):

public <T> void doSomething() {

BaseTemplate obj = ...
Class<T> newType = Class('AddSample');
T t = newType.cast(obj);

Без типизации метода вы не можете связать динамический класс с типом, который будет приведен к типу.

Другие вопросы по тегам