Как вызвать конструктор с помощью LambdaMetaFactory?

Я хочу попытаться избежать отражения при вызове конструктора, и я пытаюсь следовать подходу LamdaMetaFactory, принятому в этом сообщении - Более быстрые альтернативы отражению Java

Мой класс, который я хочу построить, выглядит так:

interface DBBroker {}

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

Используя LambaMetaFactory, я пытаюсь построить BiFunction<BrokerPool, Configuration, DBBroker>, чтобы заменить прямой вызов конструктора.

Мой код пока выглядит так:

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);

К сожалению, это возвращает ошибку -

AbstractMethodError: Метод org / exist / storage / BrokerFactory $$ Lambda $ 55.apply (Ljava / lang / Object; Ljava / lang / Object;) Ljava / lang / Object; абстрактно.

Я пробовал изменять различные типы, переданные в LambdaMetafactory.metafactory, но, похоже, не могу понять это правильно, а документ Javadoc, конечно, нелегко понять.

Может кто-нибудь посоветует, пожалуйста?


person adamretter    schedule 07.05.2018    source источник


Ответы (1)


Ошибка, которую вы сделали, связана с используемым вами типом SAM. Для этого должен использоваться стертый тип метода apply, чтобы он был

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

Но вы также можете использовать mh.type().generic(), который возвращает то же самое:

BiFunction<BrokerPool, Configuration, DBBroker> constructor =
(BiFunction<BrokerPool, Configuration, DBBroker>)
    LambdaMetafactory.metafactory(
                lookup, "apply", methodType(BiFunction.class),
                mh.type().generic(), mh, mh.type()).getTarget().invokeExact();
//              ^^^^^^^^^^^^^^^^^^^
person Jorn Vernee    schedule 07.05.2018
comment
Обычно классы, созданные LambdaMetafactory, более эффективны, чем прокси, предоставляемые MethodHandleProxies, поэтому вопрос не в том, нужен ли вам LambdaMetafactory - person Holger; 07.05.2018
comment
@Holger Ха, да, я сам просто подумал о реализации и посмотрел на нее. Он просто использует Proxy.newInstance внутри: / Я все еще думаю, что это проще, но поскольку цель - производительность, я удалю эту часть. - person Jorn Vernee; 07.05.2018
comment
@JornVernee mh.type().generic() - это то, что я хотел опубликовать перед вашим редактированием! +1 - person Eugene; 07.05.2018
comment
В большинстве случаев erase() ближе к намерению как generic(). - person Holger; 07.05.2018
comment
@JornVernee Теперь интересно, как это сделать, когда количество аргументов конструктора неизвестно до времени выполнения. например моя функция конструкции поставляется с: Class ‹?› [] paramClasses и Object [] paramValues. - person adamretter; 07.05.2018
comment
@adamretter Afaik невозможно найти дескриптор метода конструктора, если вы не знаете точный тип, хотя вы можете использовать _ 1_ для преобразования объекта Constructor<...> в дескриптор метода. Но если вы вызываете его только один раз, а затем отбрасываете, вы также можете вызвать _ 3_ напрямую. - person Jorn Vernee; 07.05.2018
comment
@adamretter Получение типа конструктора от paramClasses будет работать, пока они точно соответствуют типам параметров конструктора (и не являются подтипами). Но вы все равно не знаете, какой интерфейс использовать в качестве оболочки. Я думаю, вы могли бы просто использовать дескриптор метода напрямую. - person Jorn Vernee; 07.05.2018
comment
Спасибо, это тоже был наивный вывод - что тип интерфейса не может быть получен статически. - person adamretter; 07.05.2018