All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.fortnox.reactivewizard.util.MethodSetter Maven / Gradle / Ivy

There is a newer version: 24.6.0
Show newest version
package se.fortnox.reactivewizard.util;


import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.function.BiConsumer;

/**
 * Represents a setter method.
 */
public class MethodSetter implements Setter {

    public static  Setter create(Class cls, Method method) {
        AccessorUtil.MemberTypeInfo memberTypeInfo = AccessorUtil.setterTypeInfo(cls, method);
        return new MethodSetter<>(method, memberTypeInfo.getReturnType(), memberTypeInfo.getGenericReturnType());
    }

    private final BiConsumer setterLambda;
    private final Class parameterType;
    private final Type     genericParameterType;

    private MethodSetter(Method method, Class parameterType, Type genericParameterType) {
        this.parameterType = parameterType;
        this.genericParameterType = genericParameterType;

        MethodHandles.Lookup lookup = ReflectionUtil.lookupFor(method.getDeclaringClass(), method);

        try {
            MethodHandle methodHandle = lookup.unreflect(method);
            setterLambda = compileLambda(lookup, methodHandle);
        } catch (Throwable t) {
            throw new RuntimeException(t);
        }

    }

    private BiConsumer compileLambda(MethodHandles.Lookup lookup, MethodHandle methodHandle) throws Throwable {
        CallSite callSite = LambdaMetafactory.metafactory(
                lookup,
                "accept",
                MethodType.methodType(BiConsumer.class),
                MethodType.methodType(void.class, Object.class, Object.class),
                methodHandle,
                wrapMethodType(methodHandle.type())
        );
        return (BiConsumer) callSite.getTarget().invoke();
    }

    private MethodType wrapMethodType(MethodType methodType) {
        // JDK9+ has made changes to LambdaMetaFactory that prevent lambdas from being able to do proper
        // adaptation of types. In particular boxed types are no longer widened appropriately and
        // Object can no longer be adapted to primitive types.
        return methodType.wrap().changeReturnType(void.class);
    }

    @Override
    public void invoke(I instance, T value) {
        setterLambda.accept(instance, value);
    }

    @Override
    public Class getParameterType() {
        return parameterType;
    }

    @Override
    public Type getGenericParameterType() {
        return genericParameterType;
    }

    @Override
    public BiConsumer setterFunction() {
        return setterLambda;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy