Java lambda 元工厂

1 java lambda-metafactory

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){
     ---
}
Run Code Online (Sandbox Code Playgroud)

我收到此异常 java.lang.invoke.LambdaConversionException: 实例方法 invokeVirtual 调用的参数数量不正确:()void; 0 个捕获参数、0 个功能接口方法参数、0 个实现参数

Hol*_*ger 5

这个调用几乎有所有错误:

\n\n
    \n
  1. CallSite lambdaFactory = LambdaMetafactory.metafactory(
  2. \n
  3. \xc2\xa0\xc2\xa0lookup,
  4. \n
  5. \xc2\xa0\xc2\xa0"call",这必须是需要实现的接口方法的名称。对于BiConsumer,这是"accept"
  6. \n
  7. \xc2\xa0\xc2\xa0MethodType.methodType(BiConsumer.class),这是invokedType,它必须匹配invoke与 (10) 处的调用相匹配。
  8. \n
  9. \xc2\xa0\xc2\xa0MethodType.methodType(void.class,Long.class),这是在字节码级别(即类型擦除之后)实现的接口方法的签名。对于BiConsumer,这始终是MethodType.methodType(void.class, Object.class, Object.class)
  10. \n
  11. \xc2\xa0\xc2\xa0lookup.findVirtual(CallClass.class, "call",
  12. \n
  13. \xc2\xa0\xc2\xa0\xc2\xa0\xc2\xa0MethodType.methodType(void.class,Long.class)),
  14. \n
  15. \xc2\xa0\xc2\xa0MethodType.methodType(void.class)这是接口的特殊化,或者当接口不是通用时与 (5) 相同。无论哪种情况,参数的数量都必须与 (5) 匹配。对于您的情况,您可能希望匹配目标方法,所以它必须是MethodType.methodType(void.class, CallClass.class, Long.class).
  16. \n
  17. \xc2\xa0\xc2\xa0);
  18. \n
  19. lambdaFactory.getTarget().invoke(callId);invoke调用必须与 (4) 中指定的invokedType签名匹配。指定的参数是要捕获的值。既然你\xe2\x80\x99已经选择实现BiConsumer,它与您的目标方法具有相同的功能签名,因此无需使用附加值。
  20. \n
\n\n

最大的问题是(4)和(10)之间的不匹配,因为它使你不清楚你真正想要实现的目标。您想callId按照invoke(callId)建议捕获现有函数还是想创建一个非捕获函数,如invokedType参数和选择BiConsumer

\n\n

对于一个简单的BiConsumer生成,固定代码如下所示:

\n\n
CallSite callSite = LambdaMetafactory.metafactory(\n   lookup, "accept", MethodType.methodType(BiConsumer.class), \n   MethodType.methodType(void.class, Object.class, Object.class),\n   lookup.findVirtual(CallClass.class,"call", MethodType.methodType(void.class,Long.class)),\n   MethodType.methodType(void.class, CallClass.class, Long.class)\n);\nBiConsumer<CallClass,Long> bc = (BiConsumer<CallClass, Long>)callSite.getTarget().invoke();\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您想捕获现有的callId,则必须将功能接口更改为不需要第二个参数的类型。此外,您\xe2\x80\x99d 需要一个适配器,因为它LambdaMetafactory期望首先捕获参数,然后是接口方法参数。因此,由于不直接支持这一点,最简单的解决方案是BiConsumer<CallClass,Long>如上所述生成 a ,然后Consumer<CallClass> c = cc -> bc.apply(cc, callId);捕获现有的callId.

\n\n

仅当您还有CallClass想要绑定的现有实例时,才可以直接执行此操作:

\n\n
CallSite callSite = LambdaMetafactory.metafactory(\n   lookup, "run", MethodType.methodType(Runnable.class, LambdaMF.class, Long.class), \n   MethodType.methodType(void.class),\n   lookup.findVirtual(LambdaMF.class, "call", MethodType.methodType(void.class,Long.class)),\n   MethodType.methodType(void.class)\n);\nRunnable r = (Runnable)callSite.getTarget().invoke(callClassInstance, callId);\n
Run Code Online (Sandbox Code Playgroud)\n