Luk*_*son 6 java lambda methodhandle java-9 lambda-metafactory
我试图使用Java的LambdaMetaFactory动态实现一个通用的拉姆达Handler<RoutingContext>:
public class RoutingContext {
// ...
}
@FunctionalInterface
public interface Handler<X> {
public void handle(X arg);
}
public class HomeHandler extends Handler<RoutingContext> {
@Override
public void handle(RoutingContext ctx) {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的尝试LambdaMetaFactory:
try {
Class<?> homeHandlerClass = HomeHandler.class;
Method method = homeHandlerClass.getDeclaredMethod(
"handle", RoutingContext.class);
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(method);
MethodType factoryMethodType = MethodType.methodType(Handler.class);
MethodType functionMethodType = mh.type();
MethodHandle implementationMethodHandle = mh;
Handler<RoutingContext> lambda =
(Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup,
"handle",
factoryMethodType,
functionMethodType,
implementationMethodHandle,
implementationMethodHandle.type())
.getTarget()
.invokeExact();
lambda.handle(ctx);
} catch (Throwable e) {
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
这给出了错误:
java.lang.AbstractMethodError: Receiver class [...]$$Lambda$82/0x00000008001fa840
does not define or inherit an implementation of the resolved method abstract
handle(Ljava/lang/Object;)V of interface io.vertx.core.Handler.
Run Code Online (Sandbox Code Playgroud)
我已经尝试了一系列的其他选项functionMethodType和implementationMethodHandle,但都没有成功地得到这个工作呢.此外,即使我替换了RoutingContext.class引用Object.class,这也不能解决错误.
我可以获得lambda.handle(ctx)成功调用的唯一方法是通过更改HomeHandler使其不会扩展Handler,使HomeHandler::handle静态和更改RoutingContext.class为Object.class.奇怪的是Handler<RoutingContext>,即使它不再延伸,我仍然可以将最终的lambda投射出去Handler.
我的问题:
如何LambdaMetaFactory使用非静态方法?
对于这个非静态SAM类HomeHandler,如何在引擎盖下进行实例分配?是否LambdaMetaFactory创建接口实现的一个实例,无论有多少个方法调用,因为在这个例子中有没有捕捉到的变量?或者它是否为每个方法调用创建一个新实例?或者我应该创建一个单独的实例并以某种方式将其传递给API?
如何LambdaMetaFactory使用泛型方法?
编辑:除了下面的好答案,我还看到了这篇博文,解释了所涉及的机制:
https://medium.freecodecamp.org/a-faster-alternative-to-java-reflection-db6b1e48c33e
或者我应该创建一个单独的实例并以某种方式将其传递给API?
是.HomeHandler::handle是一个实例方法,这意味着您需要一个实例来创建一个功能的接口包装器,或者每次调用它时都要传递一个实例(因为它Handler不能用作FunctionalInterface类型).
要使用捕获的实例,您应该:
factoryMethodType也采取了HomeHandler实例functionMethodType为SAM的擦除类型,它采用Objectas参数.instantiatedMethodType参数更改为没有捕获HomeHandler实例的目标方法句柄的类型(因为它被捕获,您不再需要它作为参数).HomeHandlerto 的实例invokeExact.-
Class<?> homeHandlerClass = HomeHandler.class;
Method method = homeHandlerClass.getDeclaredMethod(
"handle", RoutingContext.class);
Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.unreflect(method);
MethodType factoryMethodType = MethodType.methodType(Handler.class, HomeHandler.class);
MethodType functionMethodType = MethodType.methodType(void.class, Object.class);
MethodHandle implementationMethodHandle = mh;
Handler<RoutingContext> lambda =
(Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup,
"handle",
factoryMethodType,
functionMethodType,
implementationMethodHandle,
implementationMethodHandle.type().dropParameterTypes(0, 1))
.getTarget()
.invokeExact(new HomeHandler()); // capturing instance
lambda.handle(ctx);
Run Code Online (Sandbox Code Playgroud)
当然,自HomeHandler实现以来Handler,您可以直接使用捕获的实例;
new HomeHandler().handle(ctx);
Run Code Online (Sandbox Code Playgroud)
或者利用编译器生成metafactory代码,它也使用invokedynamic,这意味着CallSite返回的LambdaMetafactory.metafactory只会被创建一次:
Handler<RoutingContext> lambda = new HomeHandler()::handle;
lambda.handle(ctx);
Run Code Online (Sandbox Code Playgroud)
或者,如果功能接口类型是静态知道的:
MethodHandle theHandle = ...
Object theInstance = ...
MethodHandle adapted = theHandle.bindTo(theInstance);
Handler<RoutingContext> lambda = ctxt -> {
try {
adapted.invokeExact(ctxt);
} catch (Throwable e) {
throw new RuntimeException(e);
}
};
lambda.handle(new RoutingContext());
Run Code Online (Sandbox Code Playgroud)
既然你说“很遗憾 LambdaMetaFactory API 如此复杂”,那么应该提到它可以做得更简单。
首先,在使用时LambdaMetaFactory,直接使用它:
Lookup lookup = MethodHandles.lookup();
MethodType fType = MethodType.methodType(void.class, RoutingContext.class);
MethodHandle mh = lookup.findVirtual(HomeHandler.class, "handle", fType);
Handler<RoutingContext> lambda = (Handler<RoutingContext>) LambdaMetafactory.metafactory(
lookup, "handle", MethodType.methodType(Handler.class, HomeHandler.class),
fType.erase(), mh, fType).getTarget().invokeExact(new HomeHandler());
Run Code Online (Sandbox Code Playgroud)
您将使用绑定接收器调用实例方法,并且不包括接收器的目标方法的类型与instantiatedMethodType参数相同。此外,由于Tin的边界Handler<T>是Object,您可以简单地使用erase()该方法类型来获取samMethodType参数的擦除签名。
它并不总是那么简单。考虑将方法绑定static int method(int x)到Consumer<Integer>. 那么,samMethodType参数是(Object)void,instantiatedMethodType参数是(Integer)void,而目标方法的签名是int(int)。您需要所有这些参数来正确描述要生成的代码。考虑到其他(前三个)参数通常由 JVM 填充,这种方法已经只需要必要的最小值。
其次,如果您不需要最高性能,则可以简单地使用Proxy基于实现的实现:
MethodHandle mh = MethodHandles.lookup().findVirtual(HomeHandler.class,
"handle", MethodType.methodType(void.class, RoutingContext.class));
Handler<RoutingContext> lambda = MethodHandleProxies.asInterfaceInstance(
Handler.class, mh.bindTo(new HomeHandler()));
Run Code Online (Sandbox Code Playgroud)
这个选项甚至从 Java 7 开始就存在了
| 归档时间: |
|
| 查看次数: |
423 次 |
| 最近记录: |