There are two main approaches:
Reflection:
(clojure.lang.Reflector/invokeConstructor Klass (to-array [arg ...]))
Slow but fully dynamic.
Unpack the arguments beforehand:
(let [[arg1 arg2 ...] args] (Klass. arg1 arg2 ...))
( (Klass. ...)
is an idiomatic way of writing (new Klass ...)
, it will be converted to the last form at macro-distribution time.)
This will be faster if the compiler can determine which constructor will be used (you probably need to provide the appropriate type hints - use (set! *warn-on-reflection* true)
to make sure that you succeed).
The second approach, of course, is a bit cumbersome. If you plan to build many instances of Klass
in many places in your code, you can write the appropriate factory function. If you expect to deal with many classes this way, you can ignore the process of defining factory functions:
(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 ))))
Finally, if the process of defining factory functions should itself be completely dynamic, you can do something like Chouser's second approach to this question : define a function and not a macro and eval
something like a form with the syntax (defn ...)
above (syntax-quoted = with a reverse in front of it, I'm not sure how to include a literal in response to an SO message) except that you want to use fn
rather than defn
and maybe skip fname
. Calling the compiler will be expensive, but the return function will be executed like any Clojure function; see Chaucer's above answer for a longer discussion.
For completeness, if you are using Clojure 1.3 or later, and the Java class involved is actually a Clojure record, then the factory positional function is already created under the name ->RecordName
.
source share