lundi 4 mai 2020

Create BiConsumer from LambdaMetafactory

I'm trying to dynamically create a method reference of type BiConsumer through LambdaMetafactory. I was trying to apply two approaches found on https://www.cuba-platform.com/blog/think-twice-before-using-reflection/ - createVoidHandlerLambda and here Create BiConsumer as Field setter without reflection the Holger's answer.

However in both cases I'm having below error:

Exception in thread "main" java.lang.AbstractMethodError: Receiver class org.home.ref.App$$Lambda$15/0x0000000800066040 does not define or inherit an implementation of the resolved method abstract accept(Ljava/lang/Object;Ljava/lang/Object;)V of interface java.util.function.BiConsumer.
    at org.home.ref.App.main(App.java:20)

My code is something like this:

public class App {

    public static void main(String[] args) throws Throwable {
        MyClass myClass = new MyClass();
        BiConsumer<MyClass, Boolean> setValid = MyClass::setValid;
        setValid.accept(myClass, true);

        BiConsumer<MyClass, Boolean> mappingMethodReferences = createHandlerLambda(MyClass.class);
        mappingMethodReferences.accept(myClass, true);
    }

    @SuppressWarnings("unchecked")
    public static BiConsumer<MyClass, Boolean> createHandlerLambda(Class<?> classType) throws Throwable {
        Method method = classType.getMethod("setValid", boolean.class);
        MethodHandles.Lookup caller = MethodHandles.lookup();
        CallSite site = LambdaMetafactory.metafactory(caller,
                "accept",
                MethodType.methodType(BiConsumer.class),
                MethodType.methodType(void.class, MyClass.class, boolean.class),
                caller.findVirtual(classType, method.getName(),
                        MethodType.methodType(void.class, method.getParameterTypes()[0])),
                MethodType.methodType(void.class, classType, method.getParameterTypes()[0]));

        MethodHandle factory = site.getTarget();
        return (BiConsumer<MyClass, Boolean>) factory.invoke();
    }

    public static <C, V> BiConsumer<C, V> createSetter(Class<?> classType) throws Throwable {
        Field field = classType.getDeclaredField("valid");
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        final MethodHandle setter = lookup.unreflectSetter(field);
        final CallSite site = LambdaMetafactory.metafactory(lookup,
                "accept", MethodType.methodType(BiConsumer.class, MethodHandle.class),
                setter.type().erase(), MethodHandles.exactInvoker(setter.type()), setter.type());
        return (BiConsumer<C, V>)site.getTarget().invokeExact(setter);
    }

}

Where MyClass looks like this:

public class MyClass {

    public boolean valid;

    public void setValid(boolean valid) {
        this.valid = valid;
        System.out.println("Called setValid");
    }
}

I will appreciate for help with this one.





Aucun commentaire:

Enregistrer un commentaire