javalambdajava-8invokedynamic

How to invoke constructor using LambdaMetaFactory?


I want to try and avoid reflection for invoking a constructor and am trying to follow the LamdaMetaFactory approach taken in this post - Faster alternatives to Java's reflection

My class that I want to construct looks like:

interface DBBroker {}

public class NativeBroker implements DBBroker {
    public NativeBroker(BrokerPool brokerPool, final Configuration configuration) {
    }
}

Using LambaMetaFactory I am trying to construct a BiFunction<BrokerPool, Configuration, DBBroker> to replace a direct call to the constructor.

My code so far looks like:

Class<? extends DBBroker> clazz =
    (Class<? extends DBBroker>) Class.forName("org.exist.storage.NativeBroker");

MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh =
    lookup.findConstructor(clazz, MethodType.methodType(void.class, new Class[] {BrokerPool.class, Configuration.class}));

BiFunction<BrokerPool, Configuration, DBBroker> constructor 
    (BiFunction<BrokerPool, Configuration, DBBroker>)
        LambdaMetafactory.metafactory(
                    lookup, "apply", MethodType.methodType(BiFunction.class),
                    mh.type(), mh, mh.type()).getTarget().invokeExact();

final DBBroker broker = constructor.apply(database, conf);

Unfortunately this returns the error -

AbstractMethodError: Method org/exist/storage/BrokerFactory$$Lambda$55.apply(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; is abstract.

I have tried modifying the various types passed to LambdaMetafactory.metafactory but I can't quite seem to get this right, and the Javadoc is certainly not easily understood.

Can someone advise please?


Solution

  • The mistake you made is in the SAM type you used. The erased type of the apply method has to be used for that, so that would be

    methodType(Object.class, Object.class, Object.class)
    

    But you could also use mh.type().generic() which returns the same thing:

    BiFunction<BrokerPool, Configuration, DBBroker> constructor =
    (BiFunction<BrokerPool, Configuration, DBBroker>)
        LambdaMetafactory.metafactory(
                    lookup, "apply", methodType(BiFunction.class),
                    mh.type().generic(), mh, mh.type()).getTarget().invokeExact();
    //              ^^^^^^^^^^^^^^^^^^^