Search code examples
javaclojure

Apply for Java constructors


I have a vector of values, say [210 50 60] and I need to create Java object with it. For example I want to create Color:

(Color. 210 50 60)         ; standard way
(apply Color. [210 50 60]) ; hmm... I need something like this

Of course Color. is not a function and we cannot use apply on it. Is there an elegant way to solve this task or do I have to write it like this:

(let [[r g b] [210 50 60]]
  (Color. r g b))

Solution

  • If you dont mind paying the reflection cost I think https://stackoverflow.com/a/9172515/151650 is an elegant way. If you don't mind crazy macros:

    (defn arity [c i]
      (let [args (map #(symbol (str "arg" %)) (range i))]
        `([~@args] (new ~c ~@args))))
    
    (defmacro ->c [c]
       (let [cs (.getConstructors (resolve c))
             arities (set (map #(count (.getParameterTypes %)) cs))
             fn-arities (map #(arity c %) arities)]
         `(fn ~@fn-arities)))
    
    (apply (->c java.awt.Color) 1 [2 3])