使用MethodHandles.Lookup,MethodHandles,MethodTypes等手动创建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; }
解决方法
引导程序方法的第三个参数(名为lambdaType)是关联的invokedynamic指令的调用类型(通常由JVM填充).它的语义是由bootstrap方法定义的,在LambdaMetaFactory的情况下,它将函数接口指定为返回类型(要构造的对象的类型),并将要捕获的值指定为参数类型(要使用的值的类型)构造一个lambda实例).
因此,为了捕获它,您必须将此类型添加到您调用的类型,并将其作为参数传递给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(),methodType); CallSite callSite = LambdaMetafactory.Metafactory(lookup,invokedType,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),Test.class,int.class); MethodHandle methodHandle=lookup.findVirtual(getClass(),"addFortyTwo",functionType,functionType); return (IntSupplier) callSite.getTarget().invokeExact(this,capture); } public int addFortyTwo(int valueToAdd) { return 42+valueToAdd; } }
目标方法将具有由此类型组成的签名(如果不是静态的),后跟所有参数类型.捕获值将从左到右映射到此签名的类型,其余参数类型(如果有)对功能签名有贡献,因此必须匹配接口方法的参数类型.
这意味着当没有捕获的值并且目标方法不是静态时,方法接收器类型可能与第一类型的功能签名相关联,如ToIntFunction< String>中所示. F =字符串::长度;.