简体   繁体   English

Java lambda 元工厂

[英]Java lambda meta factory

CallSite lambdaFactory = LambdaMetafactory.metafactory(
   lookup, 
   "call",
   MethodType.methodType(BiConsumer.class), 
   MethodType.methodType(void.class,Long.class), 
      lookup.findVirtual(CallClass.class, "call",       
   MethodType.methodType(void.class,Long.class)),
   MethodType.methodType(void.class));
lambdaFactory.getTarget().invoke(callId);


private void call(Long callId){
     ---
}

I am getting this exception java.lang.invoke.LambdaConversionException: Incorrect number of parameters for instance method invokeVirtual call:()void;我收到此异常 java.lang.invoke.LambdaConversionException: InvokeVirtual call:()void 实例方法的参数数量不正确; 0 captured parameters, 0 functional interface method parameters, 0 implementation parameters 0个捕获参数,0个功能接口方法参数,0个实现参数

There is almost everything wrong in this invocation:在这个调用中几乎一切都错了:

  1. CallSite lambdaFactory = LambdaMetafactory.metafactory(
  2. lookup,
  3. "call", this must be the name of the interface method needed to be implemented. "call",这必须是需要实现的接口方法的名称。 For BiConsumer , this is "accept" .对于BiConsumer ,这是"accept"
  4. MethodType.methodType(BiConsumer.class), this is the invokedType which must match the invoke call at (10). MethodType.methodType(BiConsumer.class),这是必须的匹配invokedType invoke在(10)调用。
  5. MethodType.methodType(void.class,Long.class), this is the signature of the interface method to implement at the bytecode level, ie after type erasure. MethodType.methodType(void.class,Long.class),这是要在字节码级别实现的接口方法的签名,即类型擦除后。 For BiConsumer , this is always MethodType.methodType(void.class, Object.class, Object.class) .对于BiConsumer ,这始终是MethodType.methodType(void.class, Object.class, Object.class)
  6. lookup.findVirtual(CallClass.class, "call",
  7. MethodType.methodType(void.class,Long.class)),
  8. MethodType.methodType(void.class) this is the specialization of the interface or identical to (5) when the interface is not generic. MethodType.methodType(void.class)这是接口的特化,或者当接口不是泛型时与 (5) 相同。 In either case, the number of parameters must match with (5).在任何一种情况下,参数的数量都必须与 (5) 匹配。 For your case, you likely want match the target method, so it must be MethodType.methodType(void.class, CallClass.class, Long.class) .对于您的情况,您可能希望匹配目标方法,因此它必须是MethodType.methodType(void.class, CallClass.class, Long.class)
  9. );
  10. lambdaFactory.getTarget().invoke(callId); the invoke call must match the invokedType signature specified in (4). invoke调用必须与 (4) 中指定的invokeType签名匹配。 The specified arguments are the values to be captured .指定的参数是要捕获的值。 Since you've chosen to implement BiConsumer , which has the same functional signature as your target method, there is no use for an additional value.由于您选择实现BiConsumer ,它与您的目标方法具有相同的功能签名,因此附加值没有用。

The biggest problem is the mismatch between (4) and (10), as it makes it unclear what you actually want to achieve.最大的问题是 (4) 和 (10) 之间的不匹配,因为它使您不清楚您真正想要实现的目标。 Do you want to capture an existing callId as invoke(callId) suggests or do you want to create a non-capturing function, as the invokedType argument and the choice of BiConsumer suggest?你想捕捉到一个现有的callIdinvoke(callId)的建议,或你想创建一个非捕获功能,为invokedType参数和选择BiConsumer建议?

For a straight-forward BiConsumer generation, the fixed code would look like:对于直接的BiConsumer生成,固定代码如下所示:

CallSite callSite = LambdaMetafactory.metafactory(
   lookup, "accept", MethodType.methodType(BiConsumer.class), 
   MethodType.methodType(void.class, Object.class, Object.class),
   lookup.findVirtual(CallClass.class,"call", MethodType.methodType(void.class,Long.class)),
   MethodType.methodType(void.class, CallClass.class, Long.class)
);
BiConsumer<CallClass,Long> bc = (BiConsumer<CallClass, Long>)callSite.getTarget().invoke();

If you want to capture an existing callId , you would have to change the functional interface to a type not expecting a second argument.如果要捕获现有的callId ,则必须将功能接口更改为不需要第二个参数的类型。 Further, you'd need an adapter, because the LambdaMetafactory expects captured arguments first and the interface method arguments afterwards.此外,您需要一个适配器,因为LambdaMetafactory需要捕获的参数,然后是接口方法参数。 So, since this is not supported directly, the simplest solution is to generate a BiConsumer<CallClass,Long> as above, followed by Consumer<CallClass> c = cc -> bc.apply(cc, callId);所以,由于这不是直接支持的,最简单的解决方案是生成一个BiConsumer<CallClass,Long>如上所述,然后是Consumer<CallClass> c = cc -> bc.apply(cc, callId); , capturing the existing callId . ,捕获现有的callId

Only if you also have an already existing CallClass instance you want to bind, you can do it directly:只有当您还有一个已经存在的CallClass实例想要绑定时,您才可以直接进行绑定:

CallSite callSite = LambdaMetafactory.metafactory(
   lookup, "run", MethodType.methodType(Runnable.class, LambdaMF.class, Long.class), 
   MethodType.methodType(void.class),
   lookup.findVirtual(LambdaMF.class, "call", MethodType.methodType(void.class,Long.class)),
   MethodType.methodType(void.class)
);
Runnable r = (Runnable)callSite.getTarget().invoke(callClassInstance, callId);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM