InvokeExact no objeto, cujo tipo é carregado dinamicamente pelo carregador de classes
Passei o dia inteiro nesse problema. Meu problema é como fazer uma chamada de MethodHandle.invokeExact em uma instância, cujo tipo de classe é carregado dinamicamente no tempo de execução do programa. Para tornar o problema mais claro, mostro meu código de exemplo abaixo:
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);
Nesta amostra, expClass é carregado dinamicamente e seu tipo de classe éAddSample
. A instância obj na próxima linha é declarada como BaseTemplate e seu tipo real éAddSample
. A classe AddSample é uma subclasse de BaseTemplate. Em seguida, um MethodHandle myMh é criado para a função add deAddSample
mas a chamada de myMH falha devido a receiverType não corresponde.
omyMH.invokeExact
gera erro de tempo de execução
java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int
porque o receptor destemyMH
é declarado em expClass (AddSample), mas o receptorobj
A corrente fornecida é declarada BaseTemaplte, embora a classe do objeto seja AddSample. InvokeExact requer correspondência exata de parâmetros.
Meu problema pode ser simplificado da seguinte maneira: como converter uma instância do tipo base para um tipo filho carregado dinamicamente?
BaseTemplate obj = ...
Class<?> newType = Class('AddSample') //dynamic loaded...
alterar o tipo declarado do objeto para AddSample, que é carregado dinamicamente ..?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);
Usar elenco não ajuda a resolver o problema, que é o mesmo resultado anterior. O motivo ainda é que o parâmetro fornecido não corresponde exatamente à declaração myMH. Ficaria mais claro quando eu verifico os bytecodes gerados:
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
o myMH aponta para(AddSample,String,int,float)int
, mas com parâmetros:(Object, String, int, float)
, e este resultado em erro de tempo de execução, que foi mostrado anteriormente.
obrigado