В текущей реализации кэширование сгенерированных классов (или даже экземпляров для не захватывающих лямбда-выражений) является свойством инструкции invokedynamic
, которая будет повторно использовать результат начальной загрузки, выполненной в первое исполнение.
Сам метод начальной загрузки, размещенный в классе LambdaMetafactory
, будет генерировать новый класс каждый раз, когда он вызывается. Поэтому, когда вы используете эту фабрику напрямую, вы будете получать новый класс при каждом вызове в соответствии с текущей реализацией.
public <In, Out, A> BiFunction<In, Out, Out> weave(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer) {
MethodHandles.Lookup l = MethodHandles.lookup();
try {
MethodHandle target = l.findStatic(l.lookupClass(), "weaveLambdaBody",
MethodType.methodType(Object.class, Function.class, BiConsumer.class,
Object.class, Object.class));
MethodType t = target.type().dropParameterTypes(0, 2);
return (BiFunction<In, Out, Out>)LambdaMetafactory.metafactory(l, "apply",
target.type().dropParameterTypes(2, 4).changeReturnType(BiFunction.class),
t, target, t) .getTarget().invokeExact(getter, consumer);
}
catch(RuntimeException | Error e) {
throw e;
}
catch(Throwable t) {
throw new IllegalStateException(t);
}
}
private static <In, Out, A> Out weaveLambdaBody(
Function<? super In, A> getter,
BiConsumer<? super Out, ? super A> consumer,
In in, Out out) {
consumer.accept(out, getter.apply(in));
return out;
}
Во-первых, вы должны десугарировать лямбда-тело в метод. Захваченные значения идут первыми в списке параметров, за которыми следуют параметры типа функционального интерфейса. LambdaMetafactory
содержит исчерпывающую документацию по его использованию.
Хотя я сохранил параметры типа для документирования, должно быть ясно, что при такой операции вы теряете безопасность во время компиляции.