title: Clojure macro for calling Java setters based on a map?
body: <p>I'm writing a Clojure wrapper for the Braintree Java library to provide a more concise and idiomatic interface.  I'd like to provide functions to instantiate the Java objects quickly and concisely, like:</p>
  
  <pre><code>(transaction-request :amount 10.00 :order-id "user42")
  </code></pre>
  
  <p>I know I can do this explicitly, as shown in <a href="https://stackoverflow.com/questions/4314718/clojure-idiomatic-way-to-initialize-a-java-object">this question</a>:</p>
  
  <pre><code>(defn transaction-request [&amp; {:keys [amount order-id]}]
    (doto (TransactionRequest.)
      (.amount amount)
      (.orderId order-id)))
  </code></pre>
  
  <p>But this is repetitive for many classes and becomes more complex when parameters are optional.  Using reflection, it's possible to define these functions much more concisely:</p>
  
  <pre><code>(defn set-obj-from-map [obj m]
    (doseq [[k v] m]
      (clojure.lang.Reflector/invokeInstanceMethod
        obj (name k) (into-array Object [v])))
    obj)
  
  (defn transaction-request [&amp; {:as m}]
    (set-obj-from-map (TransactionRequest.) m))
  
  (defn transaction-options-request [tr &amp; {:as m}]
    (set-obj-from-map (TransactionOptionsRequest. tr) m))
  </code></pre>
  
  <p>Obviously, I'd like to avoid reflection if at all possible.  I tried defining a macro version of <code>set-obj-from-map</code> but my macro-fu isn't strong enough.  It probably requires <code>eval</code> as explained <a href="https://stackoverflow.com/a/3752276/1226640">here</a>.</p>
  
  <p>Is there a way to call a Java method specified at runtime, without using reflection?</p>
  
  <p>Thanks in advance!</p>
  
  <p><strong>Updated solution:</strong></p>
  
  <p>Following the advice from Joost, I was able to solve the problem using a similar technique. A macro uses reflection at compile-time to identify which setter methods the class has and then spits out forms to check for the param in a map and call the method with it's value.</p>
  
  <p>Here's the macro and an example use:</p>
  
  <pre><code>; Find only setter methods that we care about
  (defn find-methods [class-sym]
    (let [cls (eval class-sym)
          methods (.getMethods cls)
          to-sym #(symbol (.getName %))
          setter? #(and (= cls (.getReturnType %))
                        (= 1 (count (.getParameterTypes %))))]
      (map to-sym (filter setter? methods))))
  
  ; Convert a Java camelCase method name into a Clojure :key-word
  (defn meth-to-kw [method-sym]
    (-&gt; (str method-sym)
        (str/replace #"([A-Z])"
                     #(str "-" (.toLowerCase (second %))))
        (keyword)))
  
  ; Returns a function taking an instance of klass and a map of params
  (defmacro builder [klass]
    (let [obj (gensym "obj-")
          m (gensym "map-")
          methods (find-methods klass)]
      `(fn [~obj ~m]
         ~@(map (fn [meth]
                 `(if-let [v# (get ~m ~(meth-to-kw meth))] (. ~obj ~meth v#)))
                methods)
         ~obj)))
  
  ; Example usage
  (defn transaction-request [&amp; {:as params}]
    (-&gt; (TransactionRequest.)
      ((builder TransactionRequest) params)
      ; some further use of the object
    ))
  </code></pre>