Грубый набросок решения, которое явно использует LambdaMetafactory
так, как вы, кажется, хотите:
// the signature of the method returned by LambdaMetaFactory
// it takes an object of bean's type and makes a MapHandler that calls one
// of its instance methods
MethodType mkLambdaType = MethodType.methodType(MapHandler.class, bean.getClass());
// the signature of the method in MapHandler being implemented
MethodType handleType = MethodType.methodType(Mono.class, Mono.class);
MethodHandles.Lookup lookup = MethodHandles.lookup();
// FYI this won't search in supertypes
// getMethods() will, but you only get public ones even if you have more privileged access
// you decide what to do here; the loop body is the important thing
for(Method method : bean.getClass().getDeclaredMethods()) {
if(Modifier.isStatic(method.getModifiers())) continue;
try {
MethodHandle target = lookup.unreflect(method);
CallSite mkLambda = LambdaMetafactory.metafactory
(lookup, "handle", mkLambdaType, handleType, target, handleType);
list.add((MapHandler)mkLambda.getTarget().invoke(bean));
} catch(IllegalAccessException | LambdaConversionException e) {
// because I am lazy, I'm not checking that method has the correct type
// I'm letting LambdaMetafactory throw if that's not the case
// if you choose not to use LambdaMetafactory, you may have to implement
// this type-checking
continue;
} catch(Throwable t) {
// Throwables come from the MethodHandle#invoke call
// but nothing should be thrown at all, because LambdaMetafactory
// produces its errors from metafactory, early, which are caught above
throw new RuntimeException("Unexpected error during reflection", t);
}
}
Я считаю, что это очень расточительно.Обычная реализация LambdaMetafactory
, скорее всего, создаст целый новый класс в вызове metafactory
, возвращая CallSite
, указывающий на конструктор или подобноеЭто означает, что каждый MapHandler
, который вы получаете, является собственным анонимным классом, созданным во время выполнения.Напротив, ваша первоначальная идея использовать лямбду для вызова Method
гораздо приятнее JVM.Лямбда вызывает создание одного LambdaMetafactory
класса, который содержит method
и bean
в качестве переменных экземпляра.После первого прогона кода, где invokedynamic
загружается, каждый MapHandler
создается просто путем создания экземпляра этого анонимного класса.Мое LambdaMetafactory
решение кажется приемлемым, только если вам нужно относительно немного MapHandler
с, но каждое вызывается так часто, что накладные расходы Method#invoke
слишком высоки.
Так что я пошел вперед и сделал несколько быстрых тестов.В вашем случае, когда вы инициализируете MapHandler
s при запуске программы, а затем просто вызываете их, моя и ваша техника примерно одинаковы.Они оба настолько быстры, что я не могу измерить разницу во времени, чтобы вызвать различные виды MapHandler
.В целом, LambdaMetafactory
хуже, потому что создание анонимного класса занимает много времени.На самом деле, чем больше занятий вы проводите, тем дольше это занимает.Между тем, method.invoke
лямбда просто должна построить один объект, который часто в тысячи раз быстрее.