使用LambdaMetafactory无法破解MethodHandle?

问题描述 投票:0回答:1

我想使用 lambdametafactory 将 Record 的构造函数转换为 Function(T 是泛型类型),这是我的代码:

public record R(
        String a,
        String b
) {
}

private static void testRecord() throws Throwable {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodHandle constructor = lookup.findConstructor(R.class, MethodType.methodType(void.class, String.class, String.class));
    constructor = constructor.asSpreader(Object[].class, 2);
    R r = (R) constructor.invokeExact(new Object[]{"a", "b"});
    System.out.println(r.a());
    System.out.println(r.b());
    MethodType methodType = constructor.type();
    CallSite callSite = LambdaMetafactory.metafactory(lookup,
            "apply",
            MethodType.methodType(Function.class),
            methodType.erase(),
            constructor,
            methodType);
    Function<Object[], R> f = (Function<Object[], R>) callSite.getTarget().invokeExact();
    R apply = f.apply(new Object[]{"a", "b"});
    System.out.println(apply.a());
    System.out.println(apply.b());
} 

使用

constructor.invokeExact()
方法时,可以成功实例化记录,但
CallSite
无法通过
LambdaMetafactory.metafactory()
方法生成,因为出现以下错误:

Exception in thread "main" java.lang.invoke.LambdaConversionException: MethodHandle(Object[])R is not direct or cannot be cracked
    at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.<init>(AbstractValidatingLambdaMetafactory.java:143)
    at java.base/java.lang.invoke.InnerClassLambdaMetafactory.<init>(InnerClassLambdaMetafactory.java:168)
    at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:336)

我该如何解决这个问题?

java reflection record methodhandle lambda-metafactory
1个回答
0
投票

直接方法句柄是那些直接引用 Java 方法的方法句柄,但是,您的方法句柄则不然,因为您使用了适配器:

constructor = constructor.asSpreader(Object[].class, 2);

因此,它不能与

LambdaMetafactory
结合使用。

LMF 是一个运行时 API,用作 lambda 和方法引用语言功能的实现。它并不是真的要直接使用,所以正如您所发现的,它有一些锋利的边缘。


对于间接方法句柄,您的选项是

MethodHandleProxies
:

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle constructor = lookup.findConstructor(R.class, MethodType.methodType(void.class, String.class, String.class));
constructor = constructor.asSpreader(Object[].class, 2);
Function<Object[], R> f = (Function<Object[], R>) MethodHandleProxies(Function.class, constructor);

使用 lambda 表达式来包装方法句柄:

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle constructor = lookup.findConstructor(R.class, MethodType.methodType(void.class, String.class, String.class));
constructor = constructor.asSpreader(Object[].class, 2);
Function<Object[], R> f = args -> {
    try {
        return (R) constructor.invokeExact(args);
    catch (Throwable t) {
        throw new IllegalStateException("Should no happen", t);
    }
};

或者您可以使用自己的类来实现接口并使用 ASM 等字节码库包装方法句柄。

© www.soinside.com 2019 - 2024. All rights reserved.