所以我正在尝试创建一个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"])))
答案 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"