已给予:

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");

    }
}

为什么runFunctionrunBuilder2可以工作,而runBuilder1抛出以下异常?

Java.lang.AbstractMethodError:Receiver类Testcase$$Lambda$233/0x0000000800d21d88没有定义或继承接口MyBuilder1的已解析方法‘Abstrative java.lang.Object Apply(java.lang.String)’的实现.

既然IllegalArgumentException构造函数接受的是String参数,而不是Object参数,那么JVM不应该接受runBuilder1并抱怨其他两个参数的类型吗?

推荐答案

你的MyBuilder1<R>有一个函数方法

R apply(String message);

其擦除类型为

Object apply(String message);

换句话说,与FunctionMyBuilder2不同,擦除的参数类型是String,而不是Object.erase() method of MethodType只是将所有引用类型替换为Object,这对FunctionMyBuilder2很方便,但不再适用于MyBuilder1.对于非普通类型,没有类似的简单方法.您必须包含专门针对您的情况的类型转换代码(除非您希望通过反射查找接口方法).

例如,我们可以只将返回类型更改为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确定了所需的类型,即从泛型接口类型派生的类型.必要时,生成的代码可能具有从擦除的类型到此类型的类型强制转换.由于此类型在所有情况下都与构造函数签名匹配,因此所有变量都可以调用构造函数.

Java相关问答推荐

获取拦截器内部的IP地址

Jooq隐式地将bigint转换为数字,并且索引不起作用

具有默认分支的JUnit代码覆盖率切换声明

使用Java Streams API比较两个不同的Java集合对象和一个公共属性

如何粘合(合并)文件Lucene?

Java FX中的河内之塔游戏-在游戏完全解决之前什么都不会显示

垃圾回收器是否真的删除超出作用域的对象?

为什么StandardOpenOption.CREATE不能通过Ubuntu在中小企业上运行?

如果第一位数字和最后一位数字相差超过一位,您将如何获得随机数?

JFree Chart从图表中删除边框

在Spring Boot JPA for MySQL中为我的所有类创建Bean时出错?

有没有办法在o(log(N))中以系统的方式将数组中的小块元素复制和移动到新增长的数组中的左侧?

判断重复的两个二维表算法?

Win32函数的JNA绑定DwmGetColorizationColor返回E_INVALIDARG错误

在单例类上获取Java锁,了解原因

使用同步方法中的新线程调用同步方法

Java返回生成器的实现

如何判断元素计数并在流的中间抛出异常?

在JSON上获取反斜杠

为什么Java编译器为没有参数的方法(getter方法)创建桥接方法