Вызов Java новый со списком аргументов конструктора вместо самих аргументов (в Clojure) - PullRequest
6 голосов
/ 31 января 2012

Я знаю, что могу создать такой экземпляр Java-класса в Clojure:

(new Classname args*)

Предположим, мне передан список аргументов, которые использует конструктор.Как мне создать экземпляр класса?Я не могу использовать apply, поскольку new не является функцией.

1 Ответ

8 голосов
/ 31 января 2012

Существует два основных подхода:

  1. Отражение:

    (clojure.lang.Reflector/invokeConstructor Klass (to-array [arg ...]))
    

    Медленно, но полностью динамично.

  2. Предварительно распакуйте аргументы:

    (let [[arg1 arg2 ...] args]
      (Klass. arg1 arg2 ...))
    

    ((Klass. ...) - идиоматический способ записи (new Klass ...); он преобразуется в последнюю форму во время расширения макроса.)

    Это будетбыстрее, если компилятор может определить, какой конструктор будет использоваться (вам, вероятно, потребуется предоставить соответствующие подсказки типов - используйте (set! *warn-on-reflection* true), чтобы убедиться, что вы правильно поняли).

Второй подход, конечно, немного громоздкий.Если вы планируете создавать множество Klass экземпляров во многих местах вашего кода, вы можете написать соответствующую фабричную функцию.Если вы собираетесь работать со многими классами таким образом, вы можете абстрагироваться от процесса определения фабричных функций:

(defmacro deffactory [fname klass arg-types]
  (let [params (map (fn [t]
                      (with-meta (gensym) {:tag t}))
                    arg-types)]
    `(defn ~(with-meta fname {:tag klass}) ~(vec params)
       (new ~klass ~@params))))

Наконец, если сам процесс определения фабричных функций должен быть полностью динамичным, вы можетесделайте что-то вроде второго подхода Чузера к этому вопросу : определите функцию, а не макрос и задайте ей eval что-то вроде приведенной выше формы синтаксиса (defn ...) (синтаксис-цитирования = с обратным ключом передэто; я не уверен, как включить буквальный обратный удар в SO-сообщение), за исключением того, что вы захотите использовать fn вместо defn и, возможно, пропустить fname.Вызов компилятору будет дорогостоящим, но возвращаемая функция будет работать так же, как и любая функция Clojure;см. вышеупомянутый ответ Chouser для более продолжительного обсуждения.

Ради полноты, если вы используете Clojure 1.3 или более позднюю версию, а задействованный Java-класс на самом деле является записью Clojure, то позиционная фабричная функция будетуже были созданы под именем ->RecordName.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...