4

I have a simple Person class with a getName() that returns a String:

public class Person {

    public String getName() {...}

}

How do I use LambdaMetafactory to create a lambda for that non-static method getName() at runtime?

Here's what I got this far:

public class MyMain {

    public static void main(String[] args) throws Throwable {
        GetterFunction getterFunction;

        MethodHandles.Lookup lookup = MethodHandles.lookup();
        String invokedMethodName = "getName";
        MethodType invokedType = MethodType.methodType(GetterFunction.class);
        MethodType methodType = MethodType.methodType(Object.class);
        MethodHandle virtual = lookup.findVirtual(Person.class, "getName", MethodType.methodType(String.class));
        CallSite site = LambdaMetafactory.metafactory(lookup,
                invokedMethodName,
                invokedType,
                methodType,
                virtual,
                methodType);
        getterFunction = (GetterFunction) site.getTarget().invokeExact();
        System.out.println(getterFunction.getName(new Person("Ann")));
    }

    @FunctionalInterface
    private interface GetterFunction {

        String getName(Person person);

    }

}

Which throws:

java.lang.invoke.LambdaConversionException: Incorrect number of parameters for instance method invokeVirtual foo.Person.getName:()String; 0 captured parameters, 0 functional interface method parameters, 0 implementation parameters
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:193)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)

2 Answers 2

3

Just extending Geoffrey De Smet's answer, to show what it would look like with Function, as there is a minor adjustment needed:

public class MyMain {

    public static void main(String[] args) throws Throwable {
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        CallSite site = LambdaMetafactory.metafactory(lookup,
                "apply",
                MethodType.methodType(Function.class),
                MethodType.methodType(Object.class, Object.class), // Function::apply signature
                lookup.findVirtual(Person.class, "getName", MethodType.methodType(String.class)),
                MethodType.methodType(String.class, Person.class) // Person::getName signature
        );

        Function<Person, String> getterFunction = (Function<Person, String>) site.getTarget().invokeExact();

        System.out.println(getterFunction.apply(new Person("Ann")));
    }

    static class Person {
        String name;

        public Person(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

}
1
  • Good point, I ended up doing it like this in OptaPlanner actually. See my blog optaplanner.org/blog/2018/01/09/… Do note that QuarkusIO will make all this deprecated... now we can just generate the code when the user compiles their code Commented Mar 10, 2019 at 19:59
1

This works:

public class MyMain {

    public static void main(String[] args) throws Throwable {
        GetterFunction getterFunction;
        final MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodType methodType = MethodType.methodType(String.class, Person.class);
        final CallSite site = LambdaMetafactory.metafactory(lookup,
                "invoke",
                MethodType.methodType(GetterFunction.class),
                methodType,
                lookup.findVirtual(Person.class, "getName", MethodType.methodType(String.class)),
                methodType);
        getterFunction = (GetterFunction) site.getTarget().invokeExact();
        System.out.println(getterFunction.invoke(new Person("Ann")));
    }

    @FunctionalInterface
    interface GetterFunction {

        String invoke(final Person callable);
    }

}
1
  • And you can replace the custom GetterFunction with just Function. Commented Jan 25, 2018 at 11:13

Not the answer you're looking for? Browse other questions tagged or ask your own question.