Есть два хороших способа сделать это.Что лучше, зависит от конкретных обстоятельств.
Первое - это отражение:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
Это похоже на вызов (new Integer "16")
... включает любые другие аргументы ctor, которые вам нужны в массиве to-arrayвектор.Это просто, но медленнее во время выполнения, чем использование new
с достаточным количеством подсказок типа.
Второй вариант максимально быстрый, но немного более сложный и использует eval
:
(defn make-factory [classname & types]
(let [args (map #(with-meta (symbol (str "x" %2)) {:tag %1}) types (range))]
(eval `(fn [~@args] (new ~(symbol classname) ~@args)))))
(def int-factory (make-factory "Integer" 'String))
(int-factory "42")
Ключевым моментом является получение кода, который определяет анонимную функцию, как это делает make-factory
.Это медленно - медленнее, чем в приведенном выше примере с отражением, поэтому делайте это как можно реже, например, один раз в классе.Но, сделав это, у вас есть обычная функция Clojure, которую вы можете хранить где-нибудь, в переменной, например, int-factory
в этом примере, или в хэш-карте или векторе, в зависимости от того, как вы будете ее использовать.Несмотря на это, эта заводская функция будет работать на полной скорости компиляции, может быть встроена в HotSpot и т. Д. И всегда будет работать на быстрее , чем пример отражения.
Когда вы имеете дело склассы, сгенерированные deftype
или defrecord
, вы можете пропустить список типов, так как эти классы всегда имеют ровно два ctors с разными арностями.Это позволяет что-то вроде:
(defn record-factory [recordname]
(let [recordclass ^Class (resolve (symbol recordname))
max-arg-count (apply max (map #(count (.getParameterTypes %))
(.getConstructors recordclass)))
args (map #(symbol (str "x" %)) (range (- max-arg-count 2)))]
(eval `(fn [~@args] (new ~(symbol recordname) ~@args)))))
(defrecord ExampleRecord [a b c])
(def example-record-factory (record-factory "ExampleRecord"))
(example-record-factory "F." "Scott" 'Fitzgerald)