Типы В LambdaMetaFactory
я получаю исключение, когда я называю metafactory
. Он говорит:
java.lang.invoke.LambdaConversionException:
Incorrect number of parameters for instance method
invokeVirtual my.ExecuteTest$AProcess.step_1:()Boolean;
0 captured parameters,
0 functional interface method parameters,
0 implementation parameters
Я не понимаю все из документации LambdaMetafactory.metafactory
. У меня проблемы с определением правильных параметров:
- MethodHandles.Поиск абонента -- это просто
- строка invokedName -- я совершенно уверен здесь
- MethodType invokedType -- что это?
- MethodType samMethodType -- err... не уверен, что здесь
- MethodHandle implMethod -- это нормально
- MethodType instantiatedMethodType -- что это? второй раз?
Итак, это сводится к тому, каковы различия между:
- MethodType invokedType
- MethodType samMethodType
- MethodType instantiatedMethodType
мой код выглядит так:
package my;
import java.lang.invoke.*;
import java.lang.reflect.Method;
public class Execute {
public interface ProcessBase {};
@FunctionalInterface
public interface Step {
Boolean apply();
}
public Step getMethodFromStepid(ProcessBase process, int stepid) {
try {
// standard reflection stuff
final MethodHandle unreflect = caller.unreflect(method);
final String mname = "step_"+stepid;
// new java8 method reference stuff
final Method method = process.getClass().getMethod(mname);
final MethodType type=MethodType.methodType(Boolean.class);
final MethodType stepType=MethodType.methodType(Step.class);
final MethodHandles.Lookup caller = MethodHandles.lookup();
final CallSite site = LambdaMetafactory.metafactory(
caller, "apply", stepType, type, unreflect, type); // damn
// convert site to my method reference
final MethodHandle factory = site.getTarget();
final Step step = (Step) factory.invoke();
return step;
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}
}
тесты
package my;
import org.junit.Test;
import static org.junit.Assert.*;
public class ExecuteTest {
private class AProcess implements Execute.ProcessBase {
public Boolean step_1() { return true; }
public Boolean step_2() { return false; }
}
@Test
public void getMethodFromStepid() throws Exception {
final AProcess process = new AProcess();
{
final Execute.Step methodRef = instance.getMethodFromStepid(process, 1);
final boolean result = methodRef.apply();
assertTrue(result);
}
{
final Execute.Step methodRef = instance.getMethodFromStepid(process, 2);
final boolean result = methodRef.apply();
assertFalse(result);
}
}
private final Execute instance = new Execute();
}
1 ответов
первые три параметра не являются специальными для лямбда-выражений, но стандартные аргументы для методы начальной загрузки of invokedynamic
инструкция. The lookup
параметр инкапсулирует контекст вызывающего абонента,invokedName
и invokedType
параметры представляют имя и тип invokedynamic
инструкция.
это зависит от метода bootstrap, чтобы назначить больше семантики. Поскольку в этом контексте целью этой инструкции является создание экземпляра лямбда-выражения, она будет потреблять захваченные значения и производить interface
экземпляра. Так что invokedType
будут иметь типы параметров, отражающие тип захваченных значений или быть без параметров для не-захвата лямбд и иметь тип возврата, соответствующий желаемому функционального интерфейса. The invokedName
используется для указания имени метода функционального интерфейса, что необычно, поскольку оно фактически не вызывается здесь, но поскольку вызываемое имя не имеет другого значения в противном случае, этот параметр используется повторно здесь.
на samMethodType
является сигнатурой метода функционального интерфейса для реализации (на уровне байтового кода), который идентичен instantiatedMethodType
пока, например, дженерики не участвуют. В противном случае, samMethodType
будет подлежать стиранию типа, тогда как instantiatedMethodType
включает фактические аргументы типа, например, для реализации Function<String,Integer>
-
invokedType
будет иметь тип возвратаFunction
-
samMethodType
будет(Object)Object
-
instantiatedMethodType
будет(String)Integer
обратите внимание, что для вашего конкретного случая типы в основном правильные, но так как вы хотите вызвать целевой метод на предоставленном process
экземпляр, вы должны привязать его к экземпляру lambda (вы даже не пытались). К сожалению, вы не дали понять, какая у вас фактическая проблема (т. е. что вы получаете LambdaConversionException
) в вашем вопросе, поэтому я не заметил проблемы раньше.
как сказано выше,invokedType
должны содержит типы значений для записи в качестве типов параметров. Затем вы должны пройти фактический process
экземпляр invoke
звонок. Как следует из названия, invokedType
должен соответствовать типу invoke
:
public Step getMethodFromStepid(ProcessBase process, int stepid) {
try {
// standard reflection stuff
final String mname = "step_"+stepid;
final Method method = process.getClass().getMethod(mname);
// new java8 method reference stuff
final MethodType type=MethodType.methodType(Boolean.class);
// invokedType: bind process, generate Step
final MethodType stepType=MethodType.methodType(Step.class,process.getClass());
final MethodHandles.Lookup caller = MethodHandles.lookup();
final MethodHandle unreflect = caller.unreflect(method);
final CallSite site = LambdaMetafactory.metafactory(
caller, "apply", stepType, type, unreflect, type);
// convert site to my method reference
final MethodHandle factory = site.getTarget();
// pass the value to bind and get the functional interface instance
final Step step = (Step)factory.invoke(process);
return step;
} catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
}