I'm trying to explicitly use LambdaMetafactory.metafactory, I can't understand why it works only with the Runnable functional interface. For Example this code does what it is expected (it prints "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"); } }
The problem arise when I try to use a different functional interface, such as Supplier. The following code does not work:
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<String> r = (Supplier<String>) 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)
Shouldn't the two snippet of code work in a similar way, which is the problem in the second snippet of code?
Moreover the following code, that should be equivalent, works well:
public class MetafactoryTest { public static void main(String[] args) throws Throwable { Supplier<String> r = (Supplier<String>) () -> print(); System.out.println(r.get()); } private static String print() { return "hello world"; } }
Edit
Another solution that avoids to change the method return type is to define a new functional interface:
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"; }
public class LambdaMetafactory extends Object. Methods to facilitate the creation of simple "function objects" that implement one or more interfaces by delegation to a provided MethodHandle , possibly after type adaptation and partial evaluation of arguments.
A method handle is a typed, directly executable reference to an underlying method, constructor, field, or similar low-level operation, with optional transformations of arguments or return values. These transformations are quite general, and include such patterns as conversion, insertion, deletion, and substitution.
The difference between Runnable and Supplier is that Supplier uses a generic type.
At runtime Supplier doesn't have a String get() method, it has Object get(). But the method you implement returns a String. You need to distinguish between those 2 types. Like this:
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<String> r = (Supplier<String>) factory.invoke(); System.out.println(r.get()); } private static String print() { return "hello world"; } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With