Вызов Java new со списком конструкторов args вместо самих args (в Clojure)

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

(new Classname args*)

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

1 ответов


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

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

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

    медленно, но вполне динамичный.

  2. распакуйте аргументы заранее:

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

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

    это будет быстрее, если компилятор сможет вывести, какой конструктор будет использоваться (вам, вероятно, нужно будет предоставить соответствующие подсказки типа -- use (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 [email protected]))))

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

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