java - Lambda Metafactory 变量捕获

标签 java lambda java-8

当使用 MethodHandles.LookupMethodHandleMethodType 等手动创建 lambda 时,如何实现变量捕获?

例如,没有捕获:

public IntSupplier foo() {
    return this::fortyTwo;
}
/**
 *  Would not normally be virtual, but oh well.
 */
public int fortyTwo() {
    return 42;
}

及其笨拙的形式,使用 java.lang.invoke 中的内容:

public IntSupplier foo() {
    MethodHandles.Lookup lookup = MethodHandles.lookup();
    MethodType methodType = MethodType.methodType(int.class),
               lambdaType = MethodType.methodType(IntSupplier.class);
    MethodHandle methodHandle = lookup.findVirtual(getClass(), "fortyTwo", methodType);
    CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt", lambdaType, methodType, methodHandle, methodType);
    return (IntSupplier) callSite.getTarget().invokeExact();
}
/**
 *  Would not normally be virtual, but oh well.
 */
public int fortyTwo() {
    return 42;
}

会返回一个简单的、无意义的 IntSupplier,它在调用时返回 42,但是如果有人想捕获一些东西怎么办?

最佳答案

bootstrap 方法的第三个参数,您将其命名为 lambdaType , 是关联 invokedynamic调用类型指令(通常由 JVM 填写)。它的语义由 Bootstrap 方法定义,在 LambdaMetaFactory 的情况下,它将功能接口(interface)指定为返回类型(要构造的对象的类型),并将要捕获的值指定为参数类型(构造 lambda 实例时要使用的值的类型)。

所以为了捕获this ,您必须添加 this 的类型到您调用的类型并传递 this作为 invokeExact 的参数调用:

public class Test {
    public static void main(String... arg) throws Throwable {
        System.out.println(new Test().foo().getAsInt());
    }
    public IntSupplier foo() throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType  = MethodType.methodType(int.class),
                   invokedType = MethodType.methodType(IntSupplier.class, Test.class);
        MethodHandle methodHandle = lookup.findVirtual(getClass(), "fortyTwo", methodType);
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt",
            invokedType, methodType, methodHandle, methodType);
        return (IntSupplier) callSite.getTarget().invokeExact(this);
    }
    public int fortyTwo() {
        return 42;
    }
}

如果你想捕获更多的值,你必须以正确的顺序将它们添加到签名中。例如,捕获另一个 int值:

public class Test {
    public static void main(String... arg) throws Throwable {
        System.out.println(new Test().foo(100).getAsInt());
    }
    public IntSupplier foo(int capture) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType = MethodType.methodType(int.class, int.class),
            functionType = MethodType.methodType(int.class),
            invokedType = MethodType.methodType(IntSupplier.class, Test.class, int.class);
        MethodHandle methodHandle=lookup.findVirtual(getClass(),"addFortyTwo",methodType);
        CallSite callSite = LambdaMetafactory.metafactory(lookup, "getAsInt",
            invokedType, functionType, methodHandle, functionType);
        return (IntSupplier) callSite.getTarget().invokeExact(this, capture);
    }
    public int addFortyTwo(int valueToAdd) {
        return 42+valueToAdd;
    }
}

目标方法将具有由 this 组成的签名键入,如果不是 static ,后跟所有参数类型。捕获值将从左到右按顺序映射到此签名的类型,其余参数类型(如果有)有助于功能签名,因此必须匹配 interface方法的参数类型。

这意味着当没有捕获值并且目标方法不是 static 时,方法接收者类型可能会与功能签名的第一种类型相关联,如 ToIntFunction<String> f=String::length; .

关于java - Lambda Metafactory 变量捕获,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30632574/

相关文章:

c# - QueryOver 查询中的重要顺序

c# - 使用反射创建 lambda 表达式,如 x => new { .. }

java - Spring OutputStream - 使用 IE 下载 pptx

java-8 - 如何在纯 Java 8 中获取给定日历周的所有周日期列表?

java - 使用 Java 和 Nashorn 编写脚本的新手,有教程吗?

java - 使用 JExcelApi 将包含时间的 Excel 单元格值报告为 12 小时而不是 24 小时

在Redis中存储关系数据的Java框架

java - 在 TCP 堆栈中非阻塞的 connect() 调用是什么意思 (java)

c# - Expression.ToString() 有效吗?

Java subList ConcurrentModificationException();