显式使用LambdaMetafactory

我正在尝试显式使用LambdaMetafactory.metafactory,我无法理解为什么它仅适用于Runnablefunction接口。 例如,此代码执行预期的操作(它打印“hello world”):

public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(void.class); MethodType invokedType = MethodType.methodType(Runnable.class); CallSite site = LambdaMetafactory.metafactory(caller, "run", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Runnable r = (Runnable) factory.invoke(); r.run(); } private static void print() { System.out.println("hello world"); } } 

当我尝试使用不同的function界面时会出现问题,例如Supplier。 以下代码不起作用:

 public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Supplier.class); CallSite site = LambdaMetafactory.metafactory(caller, "get", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Supplier r = (Supplier) factory.invoke(); System.out.println(r.get()); } private static String print() { return "hello world"; } } Exception in thread "main" java.lang.AbstractMethodError: metafactorytest.MetafactoryTest$$Lambda$1/258952499.get()Ljava/lang/Object; at metafactorytest.MetafactoryTest.main(MetafactoryTest.java:29) 

这两段代码不应该以类似的方式工作,这是第二段代码中的问题吗?

此外,以下代码应该是等效的,效果很好:

 public class MetafactoryTest { public static void main(String[] args) throws Throwable { Supplier r = (Supplier) () -> print(); System.out.println(r.get()); } private static String print() { return "hello world"; } } 

编辑

避免更改方法返回类型的另一个解决方案是定义新的function接口:

 public class MetafactoryTest { @FunctionalInterface public interface Test { String getString(); } public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Test.class); CallSite site = LambdaMetafactory.metafactory(caller, "getString", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Test r = (Test) factory.invoke(); System.out.println(r.getString()); } private static String print() { return "hello world"; } 

Runnable和Supplier之间的区别在于Supplier使用generics类型。

在运行时,Supplier没有String get()方法,它有Object get()。 但是您实现的方法返回一个String。 您需要区分这两种类型。 喜欢这个:

 public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(Object.class); MethodType actualMethodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Supplier.class); CallSite site = LambdaMetafactory.metafactory(caller, "get", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", actualMethodType), methodType); MethodHandle factory = site.getTarget(); Supplier r = (Supplier) factory.invoke(); System.out.println(r.get()); } private static String print() { return "hello world"; } } 

这是另一个更易于理解的变量名称的示例:

 public class Demo { public static void main(String[] args) throws Throwable { Consumer consumer = s -> System.out.println("CONSUMED: " + s + "."); consumer.accept("foo"); MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType lambdaBodyMethodType = MethodType.methodType(void.class, String.class); MethodHandle lambdaBody = caller.findStatic( Demo.class, "my$lambda$main$0", lambdaBodyMethodType); // Because of the type erasure we must use Object here // instead of String (Consumer -> Consumer). MethodType functionalInterfaceMethodType = MethodType.methodType(void.class, Object.class); // we must return consumer, no closure -> no additional parameters MethodType callSiteType = MethodType.methodType(Consumer.class); CallSite site = LambdaMetafactory.metafactory( // provided by invokedynamic: caller, "accept", callSiteType, // additional bootstrap method arguments: functionalInterfaceMethodType, lambdaBody, lambdaBodyMethodType); MethodHandle factory = site.getTarget(); Consumer r = (Consumer) factory.invoke(); r.accept("foo"); r.accept("bar"); } private static void my$lambda$main$0(String s) { System.out.println("CONSUMED: " + s + "."); } } 

因为LambdaMetafactory创建了一个合成工厂类,然后用于创建目标接口, callSiteType具有此工厂create()方法的类型。 这个create()方法由invokedynamic隐式调用 – LambdaMetafactory返回一个CallSite ,它具有对create方法的方法引用。 对于带闭包的lambda,你将像factory.create(capturedValue1, ..., capturedValueN)一样调用工厂,因此你必须相应地修改callSiteType