gpt4 book ai didi

java - 用于基于 map 调用 Java setter 的 Clojure 宏?

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:57:20 25 4
gpt4 key购买 nike

我正在为 Braintree Java 库编写 Clojure 包装器,以提供更简洁和惯用的界面。我想提供一些函数来快速简洁地实例化 Java 对象,例如:

(transaction-request :amount 10.00 :order-id "user42")

我知道我可以明确地做到这一点,如 this question 所示:

(defn transaction-request [& {:keys [amount order-id]}]
(doto (TransactionRequest.)
(.amount amount)
(.orderId order-id)))

但这对于许多类来说是重复的,并且当参数是可选的时变得更加复杂。使用反射,可以更简洁地定义这些函数:

(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 [& {:as m}]
(set-obj-from-map (TransactionRequest.) m))

(defn transaction-options-request [tr & {:as m}]
(set-obj-from-map (TransactionOptionsRequest. tr) m))

显然,我想尽可能避免反射。我尝试定义 set-obj-from-map 的宏版本,但我的 macro-fu 不够强大。如 here 所述,它可能需要 eval .

有没有一种方法可以在运行时调用指定的 Java 方法,而不使用反射?

提前致谢!

更新的解决方案:

根据 Joost 的建议,我能够使用类似的技术解决问题。宏在编译时使用反射来识别类具有哪些 setter 方法,然后吐出表单以检查映射中的参数并使用它的值调用该方法。

这是宏和一个使用示例:

; 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]
(-> (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 [& {:as params}]
(-> (TransactionRequest.)
((builder TransactionRequest) params)
; some further use of the object
))

最佳答案

你可以在编译时使用反射 ~ 只要你知道你正在处理的类 ~ 找出字段名称,并从中生成“静态” setter 。不久前我写了一些代码来为 getter 做很多这样的事情,你可能会觉得很有趣。参见 https://github.com/joodie/clj-java-fields (尤其是 https://github.com/joodie/clj-java-fields/blob/master/src/nl/zeekat/java/fields.clj 中的 def-fields 宏)。

关于java - 用于基于 map 调用 Java setter 的 Clojure 宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9488367/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com