创建一个使用字符串调用java函数的Clojure宏

时间:2016-05-28 20:06:55

标签: clojure macros

所以我正在尝试创建一个Clojure宏,它可以很容易地利用Builder模式与Java类互操作。

这是我到目前为止所尝试的内容。

(defmacro test-macro
  []
  (list
   (symbol ".queryParam")
   (-> (ClientBuilder/newClient)
       (.target "https://www.test.com"))
   "key1"   
   (object-array ["val1"])))

扩展到以下

(. 
  #object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget@107a5073"] 
  queryParam 
  "key1" 
  #object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;@16751ba2"])

期望的结果是:

 (.queryParam
  #object[org.glassfish.jersey.client.JerseyWebTarget 0x107a5073 "org.glassfish.jersey.client.JerseyWebTarget@107a5073"]       
  "key1" 
  #object["[Ljava.lang.Object;" 0x16751ba2 "[Ljava.lang.Object;@16751ba2"]) 

我想.导致某些内容被评估并移动?在这种情况下,解决方案是引用它。但是,如何引用评估表达式的结果?

我的目标是将map转换为构建对象的代码,方法是将maps键作为要调用的函数,将值作为传递给Java函数的参数。

我理解如何使用线程和do-to宏,但我正在尝试建立功能数据驱动的请求。我希望能够使用键作为“queryParam”并将值作为参数接收映射。通过这个我可以利用java类函数的整体只需要自己编写一个函数,并且有足够的1对1映射我不相信其他人会觉得它很神奇。

(def test-map {"target" ["https://www.test.com"]
               "path" ["qa" "rest/service"] 
               "queryParam" [["key1" (object-array ["val1"])]
                              ["key2" (object-array ["val21" "val22" "val23"])]] })

 (-> (ClientBuilder/newClient)
     (.target "https://www.test.com")
     (.path "qa")
     (.path "rest/service")
     (.queryParam "key1" (object-array ["val1"]))
     (.queryParam "key2" (object-array ["val21" "val22" "val23"])))

1 个答案:

答案 0 :(得分:2)

根据您的问题,您是否必须使用map作为构建器数据结构尚不清楚。我建议使用线程宏直接处理实现构建器模式的Java类:

(-> (ClientBuilder.)
      (.forEndpoint "http://example.com")
      (.withQueryParam "key1" "value1")
      (.build))

对于未实现构建器模式的类及其方法返回void(例如setter方法),您可以使用doto宏:

(doto (Client.)
  (.setEndpoint "http://example.com")
  (.setQueryParam "key1" "value1"))

使用map实现宏来编码Java方法调用是可能的,但很难。您必须将每个方法参数保留在一个序列(在map值中),以便能够调用具有多个参数的方法,或者具有一些约定来存储单个参数方法的参数,处理varargs,使用map来指定方法调用不保证它们将被调用的顺序等。这将为你的代码增加许多复杂性和魔力。

这是你如何实现它的:

(defmacro builder [b m]
  (let [method-calls
        (map (fn [[k v]] `(. (~(symbol k) ~@v))) m)]
    `(-> ~b
         ~@method-calls))) 

(macroexpand-1
  '(builder (StringBuilder.) {"append" ["a"]})) 
;; => (clojure.core/-> (StringBuilder.) (. (append "a"))) 

(str
  (builder (StringBuilder.) {"append" ["a"] })) 
;; => "a"