如何将方法对象动态转换为 Java 中的函数式接口?

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

我有几个类,每个类都实现一个接口。我从这些类中搜索出一种使用注释的方法。此方法返回一个布尔值,并且始终有一个对象作为参数,该对象始终继承自另一个固定对象。现在我想用这个方法创建一个功能接口。最佳当然是 Predicate,它接管上述参数。我现在已经尝试了一段时间来使用 LambdaMetafactory 来实现:

private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
    try {
        final MethodHandle handle = LOOKUP.unreflect(method);
        final MethodType signature = MethodType.methodType(boolean.class, method.getParameters()[0].getType());

        final CallSite callSite = LambdaMetafactory.metafactory(
            LOOKUP,
            "test",
            MethodType.methodType(Predicate.class, instance.getClass()),
            signature,
            handle,
            signature
        );

        return (Predicate<Parent>) callSite.getTarget().invoke(instance);
    } catch (Throwable e) {
        e.printStackTrace();
        return null;
    }
}

我现在的问题是,当我调用 Predicate 的测试方法时,会抛出一个 AbstractMethodError。当我将签名与 (boolean.class, Parent.class) 一起使用时,我得到一个 LambdaConversionException。甚至可以动态地实现它吗?如果是,如何?

java dynamic predicate functional-interface lambda-metafactory
1个回答
3
投票

因为你知道目标接口类型是

Predicate
你也可以使用 lambda 表达式:

private Predicate<Parent> toPredicate(final ExampleInterface instance, final Method method) {
    final MethodHandle handle = LOOKUP.unreflect(method)
        .bindTo(instance)
        .asType(MethodType.methodType(boolean.class, Parent.class);
    
    return parent -> {
        try {
            return (boolean) handle.invokeExact(parent);
        } catch (Throwable t) {
            throw new RuntimeException("Should not happen", t);
        }
    };
}

只有在特定调用站点只能看到接口的 1 或 2 个实现的特定情况下,使用 LambdaMetafactory 才是真正有益的。另请注意,每次创建元工厂时,都会生成一个新类(除非它可以从 CDS 存档中加载),它有自己的相关成本。这些类与定义的类加载器紧密相关,例如如果这是应用程序类加载器,这些类实际上永远不会被卸载,如果

metafactory
被调用很多,这可能会导致元空间耗尽。

你弄错的是接口方法类型。应该是

test
Predicate
方法的擦除:

final MethodHandle handle = LOOKUP.unreflect(method);

final CallSite callSite = LambdaMetafactory.metafactory(
    LOOKUP,
    "test",
    MethodType.methodType(Predicate.class, instance.getClass()),
    MethodType.methodType(boolean.class, Object.class),
    handle,
    MethodType.methodType(boolean.class, Parent.class)
);
© www.soinside.com 2019 - 2024. All rights reserved.