当前位置: 首页 > 知识库问答 >
问题:

为什么 LambdaMetafactory 在使用自定义函数接口时会失败(但函数工作正常)?

裴焱
2023-03-14

鉴于:

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;

class Testcase
{
    @FunctionalInterface
    public interface MyBuilder1<R>
    {
        R apply(String message);
    }

    @FunctionalInterface
    public interface MyBuilder2<R>
    {
        R apply(Object message);
    }

    public static void main(String[] args) throws Throwable
    {
        Class<?> clazz = IllegalArgumentException.class;

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findConstructor(clazz, MethodType.methodType(void.class, String.class));
        MethodHandle myFunctionConstructor = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(Function.class),
            mh.type().erase(),
            mh,
            mh.type()
        ).getTarget();

        MethodHandle myBuilderConstructor1 = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(MyBuilder1.class),
            mh.type().erase(),
            mh,
            mh.type()
        ).getTarget();

        MethodHandle myBuilderConstructor2 = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(MyBuilder2.class),
            mh.type().erase(),
            mh,
            mh.type()
        ).getTarget();

        @SuppressWarnings("unchecked")
        Function<String, IllegalArgumentException> functionFactory =
            (Function<String, IllegalArgumentException>) myFunctionConstructor.invokeExact();

        @SuppressWarnings("unchecked")
        MyBuilder1<IllegalArgumentException> myBuilder1Factory =
            (MyBuilder1<IllegalArgumentException>) myBuilderConstructor1.invokeExact();

        @SuppressWarnings("unchecked")
        MyBuilder2<IllegalArgumentException> myBuilder2Factory =
            (MyBuilder2<IllegalArgumentException>) myBuilderConstructor2.invokeExact();

        IllegalArgumentException runFunction = functionFactory.apply("test");
//      IllegalArgumentException runBuilder1 = myBuilder1Factory.apply("test");
        IllegalArgumentException runBuilder2 = myBuilder2Factory.apply("test");

    }
}

为什么run函数runBuilder2工作,而runBuilder1抛出以下异常?

java.lang.AbstractMethodError:接收器类test case $ $ Lambda $ 233/0x 0000000800d 21d 88未定义或继承接口MyBuilder1的已解析方法“abstract Java . lang . object apply(Java . lang . string)”的实现。

假设IllegalArgumentExc的构造函数接受一个String参数,而不是一个Object,JVM不应该接受runBuilder1并抱怨其他两个的参数类型吗?

共有1个答案

公良浩邈
2023-03-14

您的MyBuilder1

R apply(String message);

其擦除类型为

Object apply(String message);

换句话说,与函数MyBuilder2,而不是 MethodType 擦除()方法只是将所有引用类型替换为 >,这对于 代码(除非您想通过Reflection查找接口方法)。</p">

例如,我们可以将返回类型更改为Object并保留参数类型:

class Testcase
{
    @FunctionalInterface
    public interface MyBuilder1<R>
    {
        R apply(String message);
    }

    public static void main(String[] args) throws Throwable
    {
        Class<?> clazz = IllegalArgumentException.class;

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findConstructor(clazz,
            MethodType.methodType(void.class, String.class));

        MethodHandle myBuilderConstructor1 = LambdaMetafactory.metafactory(
            lookup,
            "apply",
            MethodType.methodType(MyBuilder1.class),
            mh.type().changeReturnType(Object.class), // instead of erase()
            mh,
            mh.type()
        ).getTarget();

        @SuppressWarnings("unchecked")
        MyBuilder1<IllegalArgumentException> myBuilder1Factory =
            (MyBuilder1<IllegalArgumentException>) myBuilderConstructor1.invokeExact();

        IllegalArgumentException runBuilder1 = myBuilder1Factory.apply("test");

        runBuilder1.printStackTrace();
    }

关于您的最后一个问题,擦除的类型是要实现的类型,而metafactory的最后一个参数决定了预期的类型,即从泛型接口类型派生。必要时,生成的代码可能会将类型从擦除的类型转换为此类型。由于此类型在所有情况下都与构造函数签名匹配,因此所有变体都可以调用构造函数。

 类似资料:
  • 我对Jest是新手,目前只是在玩一些现有功能的测试。我有一个函数,它获取一个数据数组(来自JSON文件),并将其映射到特定的位置,绘制点。 这是功能点: plotPoint将JSON数据作为参数。该函数之所以有效,是因为它在控制台中正确记录数据,而其他功能按预期工作。 但测试总是失败: 绘图点。测验js 返回此错误: 我认为可能函数是在jsonfile之前的测试中运行的。json已加载,因此我尝试

  • 使用scandir()函数时,我收到以下php警告: Scandir无法打开目录:公共html/page2中不允许操作。php在线3 第2页。php 我想使用这个功能来打印我的根文件夹的文件和子目录,但它不工作。 有人知道怎么修吗?

  • JSP规范中引入的EL大大简化了JSP页面的开发,使不熟悉Java语言的网页设计人员也可以通过EL表达式在JSP页面中访问很多系统级的资源,以及进行一系列的服务端操作。但EL本身的功能非常有限,为此,EL允许开发人员定义自己的函数来扩展EL的功能。 EL自定义函数实际上就是普通Java类中的方法。但作为EL自定义函数的Java类和方法必须满足如下的条件: l Java类必须被声明成public。

  • 主要内容:操作整个数据表,操作行或列,操作单一元素如果想要应用自定义的函数,或者把其他库中的函数应用到 Pandas 对象中,有以下三种方法: 1) 操作整个 DataFrame 的函数:pipe() 2) 操作行或者列的函数:apply() 3) 操作单一元素的函数:applymap() 如何从上述函数中选择适合的函数,这取决于函数的操作对象。下面介绍了三种方法的使用。 操作整个数据表 通过给 pipe() 函数传递一个自定义函数和适当数量的参

  • 由于健康检查失败,我无法进入GKE工作。我已经尝试了我能想到的所有调试步骤,包括: 已验证我没有任何配额不足 验证我的服务可以从集群内访问 验证我的服务在k8s/GKE负载均衡器后面工作。 验证检查正在传递Stackdrigs日志 ...我很乐意获得有关如何调试或修复的任何建议。详情如下! 我在GKE上设置了一个类型为的服务。通过外部IP效果很好: 然后,我尝试在同一服务上设置入口: 入口已创建,

  • 在JavaScript中使用const可以设置什么类型的值,特别是函数,有没有限制?这有效吗?虽然它确实有效,但出于任何原因,它被认为是不好的做法吗? 在ES6中,所有函数都应该这样定义吗?如果是这样,这似乎并没有流行起来。