【发布时间】:2016-09-22 13:33:09
【问题描述】:
当我调用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.Lookup 调用者——很简单
- String invokedName -- 我很确定
- MethodType invokedType -- 这是什么?
- MethodType samMethodType -- err... 这里不确定
- MethodHandle implMethod -- 没关系
- MethodType instantiatedMethodType -- 又是什么? 第二次?
所以归结为:
- MethodType 调用类型
- 方法类型 samMethodType
- MethodType 实例化方法类型
我的代码是这样的:
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();
}
【问题讨论】:
-
你到底想做什么? LMF 在很大程度上是编译器编写者的工具——将其视为“创建 lambda 字节码”。
-
@BrianGoetz 我有一种状态机,其中每个状态都由 java 代码编程,边缘当前通过方法名称和反射进行编程,即。
void step_3() { if(x) outgoing("step_4"); }。当然,您可以在这里犯错误,您只会在运行时注意到。所以我认为使用 methd refs 会更安全,即。void step_3() { if(x) outgoing(::step_4); }。然后在方法引用而不是字符串上工作。为了支持遗留代码,我需要从步骤号int i(例如5)到方法参考step_i,例如。step_5. -
您为什么不直接使用 AProcess::step_1 作为测试源中的方法引用,然后完成?我认为你把这件事弄得太复杂了......
-
@BrianGoetz 机器的状态保持不变。目前,持久性是通过将
i存储起来。然后我稍后将i取回我必须从i获取到方法引用才能执行它。我不认为我使事情过于复杂,但也许我遗漏了一些东西。引擎本身只能在方法引用上工作,当然,这是我的计划。但是为了从数据库中的状态提供状态机引擎,我必须从该状态生成方法引用。如果我可以将方法引用或整个闭包存储在数据库中,那将是我的一些东西。 ;-)(快乐的 python-stackless 程序员...)
标签: java reflection lambda java-8 method-reference