Ответ 1
Есть два хороших способа сделать это. Это лучше всего зависит от конкретного обстоятельства.
Первое - это отражение:
(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 [[email protected]] (new ~(symbol classname) [email protected]))))) (def int-factory (make-factory "Integer" 'String)) (int-factory "42")
Ключевым моментом является код eval, который определяет анонимную функцию, как это делает make-factory
. Это медленный - медленнее, чем пример отражения выше, поэтому делайте это как можно реже, например, один раз в классе. Но при этом у вас есть регулярная функция Clojure, которую вы можете хранить где-нибудь, в var, например int-factory
в этом примере, или в хэш-карте или векторе в зависимости от того, как вы будете ее использовать. Независимо от того, эта функция 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 [[email protected]] (new ~(symbol recordname) [email protected]))))) (defrecord ExampleRecord [a b c]) (def example-record-factory (record-factory "ExampleRecord")) (example-record-factory "F." "Scott" 'Fitzgerald)