Hay una clase.
class A {
Mono<Response> testMap(Mono<Request> reqMono)
}
No es la interfaz funcional
interface MapHandler {
Mono<?> handle(Mono<?> reqMono)
}
Ahora puedo escribir esto
{
A a = new A();
MapHandler handler = a::testMap;
}
Y quiero construir una herramienta que puede detectar todos los MapHandler en un frijol (objeto) y recogerlos.
He intentado esto.
List<MapHandler> list = initedList;
Method method = bean.getClass().getDeclaredMethods()[0];
list.put("methodName", req -> {
return (Mono<?>) method.invoke(bean, req);
})
¿Es posible hacerlo por MethodHandle
o LambdaMetaFactory
?
Un esbozo de una solución que utiliza de forma explícita LambdaMetafactory
en la forma en que parece que quieren es:
// 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);
}
}
Creo que esto es un gran desperdicio. Una aplicación común de la LambdaMetafactory
voluntad probable crear una clase completamente nueva en la metafactory
llamada, devolviendo un CallSite
apuntador a un constructor o similar. Esto significa que cada MapHandler
que se obtiene es su propia clase anónima, creada en tiempo de ejecución. Por el contrario, su idea original de utilizar un lambda para invocar una Method
es mucho más agradable a la JVM. La lambda provoca la creación de una sola LambdaMetafactory
clase, que sostiene method
y bean
como variables de instancia. Después de la primera carrera a través del código, en el que invokedynamic
se bootstrap, cada uno MapHandler
se crea simplemente creando una instancia de esta clase anónima. Mi LambdaMetafactory
única solución parece aceptable si sólo se necesita relativamente pocos MapHandler
s, pero cada uno se llama tan a menudo que la sobrecarga deMethod#invoke
es muy alto.
Así que he ido por delante y hecho un poco de la evaluación comparativa rápida. En el caso de uso, donde inicializar MapHandler
s al inicio del programa y luego sólo invocarlos, mi técnica y los suyos son aproximadamente los mismos. Ambos son tan rápido que no puedo medir realmente la diferencia en el tiempo necesario para llamar a los diferentes tipos de MapHandler
. En general, LambdaMetafactory
es peor, porque la creación de la clase anónima lleva mucho tiempo. De hecho, los más clases que realice, más tiempo tomará. Mientras tanto, el method.invoke
lambda simplemente tiene que construir un objeto, que a menudo es miles de veces más rápido.