【问题标题】:Creating a Clojure macro that uses a string to call a java function创建一个使用字符串调用 java 函数的 Clojure 宏
【发布时间】:2016-05-28 20:06:54
【问题描述】:

所以我正在尝试制作一个 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"]) 

我猜. 正在导致某些东西被评估和移动?在这种情况下,解决方案是引用它。但是如何引用评估表达式的结果呢?

我的目标是将映射转换为构建对象的代码,其中映射键是要调用的函数,值是传递给 Java 函数的参数。


我了解如何使用 threading 和 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"])))

【问题讨论】:

    标签: clojure macros


    【解决方案1】:

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

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

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

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

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

    你可以这样实现它:

    (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" 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多