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