zcaudate
zcaudate

Reputation: 14258

how to invoke a setter method on an object (that coerces types)

I wish to write a function that interops with java objects

(assoc-obj <object> "<Prop1>" <Val1> "<Prop2>" <Val2>)

which is the same as

(doto <object>
     (.set<Prop1> <Val1>)
     (.set<Prop2> <Val2>))

I'm working with reflection:

(let [method (->> 
              (seq (.getMethods java.util.Date))
              (filter #(= "setDate" (.getName %)))
              first)
      arr (object-array 1)
      _   (aset arr 0 (int 1))
      d   (java.util.Date.)]
  (.invoke method d arr)
  d)

but I am finding that there are problems with type coercing.

Are there better/more clojurish ways of doing this?

Upvotes: 0

Views: 103

Answers (2)

Ankur
Ankur

Reputation: 33637

It seems like a simple code transformation by looking at the example you have shown, so why not a macro:

(defmacro assoc-obj [obj & props]
  (let [psets (map (fn [[p v]] (list (symbol (str ".set" p)) v ))
                   (partition 2 props))]
    `(doto ~obj ~@psets)))

Upvotes: 1

Michał Marczyk
Michał Marczyk

Reputation: 84331

I'd use clojure.lang.Reflector/invokeInstanceMethod. Here's a REPL demo:

user=> (def d (java.util.Date.))
#'user/d
user=> d
#inst "2013-12-04T05:47:33.560-00:00"
user=> (clojure.lang.Reflector/invokeInstanceMethod
        d "setDate" (object-array (list 1)))
nil
user=> d
#inst "2013-12-01T05:47:33.560-00:00"

I'd consider everything about the reflector is an implementation detail (tweaks to method signatures might happen occasionally etc.), but this functionality is necessary in Clojure, in one form or another.

Upvotes: 1

Related Questions