Java лямбда-мета-фабрика - PullRequest
       31

Java лямбда-мета-фабрика

0 голосов
/ 19 февраля 2020
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){
     ---
}

Я получаю это исключение java .lang.invoke.LambdaConversionException: Неверное количество параметров для экземпляра метода invokeVirtual call :() void; 0 записанных параметров, 0 параметров метода функционального интерфейса, 0 параметров реализации

1 Ответ

4 голосов
/ 19 февраля 2020

В этом вызове почти все неправильно:

  1. CallSite lambdaFactory = LambdaMetafactory.metafactory(
  2. lookup,
  3. "call", это должно быть имя интерфейса метод должен быть реализован. Для BiConsumer это "accept".
  4. MethodType.methodType(BiConsumer.class), это invokedType , который должен соответствовать вызову invoke в (10).
  5. MethodType.methodType(void.class,Long.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) это специализация интерфейса или аналогично ( 5) когда интерфейс не универсальный c. В любом случае количество параметров должно совпадать с (5). В вашем случае вы, скорее всего, захотите сопоставить целевой метод, поэтому он должен быть MethodType.methodType(void.class, CallClass.class, Long.class).
  9. );
  10. lambdaFactory.getTarget().invoke(callId); вызов invoke должен совпадать с invokedType подпись, указанная в (4). Указанные аргументы - это значения, которые должны быть захвачены . Поскольку вы решили реализовать BiConsumer, который имеет ту же функциональную сигнатуру, что и целевой метод, дополнительное значение не используется.

Самая большая проблема - это несоответствие между (4) и (10), поскольку это делает неясным, чего вы на самом деле хотите достичь. Вы хотите захватить существующий callId, как предлагает invoke(callId), или вы хотите создать функцию без захвата, как аргумент invokedType и выбор BiConsumer предложить?

Для простого поколения 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();

Если вы хотите захватить существующий callId, вам придется изменить функциональный интерфейс на тип не ожидая второго аргумента. Кроме того, вам понадобится адаптер, потому что LambdaMetafactory ожидает сначала захваченные аргументы, а затем аргументы метода интерфейса. Таким образом, поскольку это не поддерживается напрямую, самое простое решение - сгенерировать BiConsumer<CallClass,Long>, как указано выше, а затем Consumer<CallClass> c = cc -> bc.apply(cc, callId);, захватив существующий callId.

Только если у вас также есть уже существующий 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);
...