Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit use of LambdaMetafactory

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";     }   
like image 385
andrebask Avatar asked Nov 06 '14 09:11

andrebask


People also ask

What is LambdaMetafactory?

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.

What is method handle in Java?

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.


1 Answers

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";     }     } 
like image 120
David ten Hove Avatar answered Oct 09 '22 06:10

David ten Hove