Es gibt zwei gute Möglichkeiten, dies zu tun. Was am besten ist, hängt von den spezifischen Umständen ab.
Die erste ist die Reflexion:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
, die wie (new Integer "16")
Aufruf ist ... schließen alle anderen Ctor Argumente, die Sie in der To-Array Vektor benötigen. Dies ist einfach, aber zur Laufzeit langsamer als die Verwendung von new
mit ausreichenden Typhinweisen.
Die zweite Option ist, so schnell wie möglich, aber ein bisschen komplizierter und verwendet 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")
Der entscheidende Punkt ist zu eval-Code, der eine anonyme Funktion definiert, wie make-factory
tut. Dies ist langsam - langsamer als das obige Beispiel der Reflexion, also nur so selten wie möglich wie einmal pro Klasse. Aber nachdem Sie das getan haben, haben Sie eine normale Clojure-Funktion, die Sie irgendwo speichern können, in einer Var wie int-factory
in diesem Beispiel, oder in einer Hash-Map oder einem Vektor, je nachdem, wie Sie es verwenden werden. Ungeachtet dessen wird diese Factory-Funktion mit voller kompilierter Geschwindigkeit ausgeführt, kann von HotSpot usw. inline ausgeführt werden und wird immer viel schneller laufen als das Reflektionsbeispiel .
Wenn Sie speziell sind mit Klassen, die durch deftype
oder defrecord
tun haben, können Sie die Typenliste überspringen, da immer diese Klassen haben genau zwei ctors mit jeweils unterschiedlichen arities. Dies ermöglicht es so etwas wie:
(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)
Ausgezeichnet! Die zweite Option ist offensichtlich eine sehr allgemeine Technik. Ich habe es schon anders benutzt. – chris