Daha özlü ve deyimsel bir arayüz sağlamak için Braintree Java kütüphanesi için bir Clojure sarıcı yazıyorum. Ben this question gösterildiği gibi, açıkça bu yapabileceğini biliyorumJava ayarlayıcılarını bir haritaya göre çağırmak için Clojure makrosu?
(transaction-request :amount 10.00 :order-id "user42")
: Java gibi, hızlı ve kesin nesneleri örneğini işlevleri sunmak istiyorum
(defn transaction-request [& {:keys [amount order-id]}]
(doto (TransactionRequest.)
(.amount amount)
(.orderId order-id)))
Ama bunun için tekrarlanan Birçok sınıf ve parametreler isteğe bağlı olduğunda daha karmaşık hale gelir. Yansıma kullanarak, çok daha kısaca bu işlevleri tanımlamak mümkündür:
(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))
Açıkçası, eğer mümkünse, yansımayı önlemek istiyorum. set-obj-from-map
'un bir makro sürümünü tanımlamayı denedim ancak makro fu'm yeterince güçlü değil. here açıklandığı gibi muhtemelen eval
gerektirir.
Yansıma kullanmadan çalışma zamanında belirtilen bir Java yöntemini çağırmanın bir yolu var mı?
Şimdiden teşekkürler!
Güncellenen çözüm:
Joost tavsiyelerine uyan ben benzer bir teknik kullanarak sorunu çözmek başardı. Bir makro, sınıfın hangi ayarlama yöntemlerini saptadığını belirlemek için derleme zamanında yansıma kullanır ve daha sonra bir haritadaki paramı denetlemek ve yöntemi değeriyle çağırmak için formlara tükürür. Sonra ~ alan adlarını anlamaya sürece size göre uğraşıyoruz sınıfı bildiği gibi ~
; 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]
[email protected](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
))
tercih ediyorum ne değildir yansıma? Neredeyse kesinlikle değil. –
Peki, bir makroyu kullanarak yansıma_uzunsuz bir metodu çevirmek mümkündür. Sadece makronun haritayı tutan bir sembolü alamadığını, ancak sadece ham haritanın kendisinin olduğunu farkettiğimde yansıma kullandım. Muhtemelen _runtime_'da yansıma yapmak istemediğimi belirtmek için daha açık olmalıydım, aşağıdaki gibi joost-diepenmaat gibi. – bkirkbri