我正在尝试编写一个宏,它将根据给定的参数调用java setter方法.
所以,例如:
(my-macro login-as-fred {"Username" "fred" "Password" "wilma"})
Run Code Online (Sandbox Code Playgroud)
可能会扩展到类似以下内容:
(doto (new MyClass)
(.setUsername "fred")
(.setPassword "wilma"))
Run Code Online (Sandbox Code Playgroud)
你会怎么建议解决这个问题?
具体来说,我无法找到构造setter方法名称的最佳方法,并让它将其解释为宏的符号.
Art*_*ldt 19
关于宏的好处是你实际上不必深入研究类或类似的东西.您只需要编写生成正确的s表达式的代码.
首先是一个生成s表达式的函数 (.setName 42)
(defn make-call [name val]
(list (symbol (str ".set" name) val)))
Run Code Online (Sandbox Code Playgroud)
然后是一个宏来生成表达式并将它们插入(〜@)doto表达式中.
(defmacro map-set [class things]
`(doto ~class ~@(map make-call things))
Run Code Online (Sandbox Code Playgroud)
因为它是一个宏,它永远不必知道它所调用的东西是什么类,甚至是它所使用的类存在.
有人(我相信 Arthur Ulfeldt)发布了一个几乎正确的答案,但现在已被删除。如果他再次发帖,请接受它而不是我的。(或接受 pmf。)这是一个工作版本:
(defmacro set-all [obj m]
`(doto ~obj ~@(map (fn [[k v]]
(list (symbol (str ".set" k)) v))
m)))
user> (macroexpand-1 '(set-all (java.util.Date.) {"Month" 0 "Date" 1 "Year" 2009}))
(clojure.core/doto (java.util.Date.) (.setMonth 0) (.setDate 1) (.setYear 2009))
user> (set-all (java.util.Date.) {"Month" 0 "Date" 1 "Year" 2009})
#<Date Fri Jan 01 14:15:51 PST 3909>
Run Code Online (Sandbox Code Playgroud)
请不要list为宏构造s表达式.这将严重损害宏观的卫生.犯错很容易,很难追查.请始终使用语法报价!虽然,在这种情况下这不是问题,但养成只使用语法引用的习惯是好的!
根据地图的来源,您可能还会考虑使用关键字作为键,使其看起来更像clojure.这是我的看法:
(defmacro configure
[object options]
`(doto ~object
~@(map (fn [[property value]]
(let [property (name property)
setter (str ".set"
(.toUpperCase (subs property 0 1))
(subs property 1))]
`(~(symbol setter) ~value)))
options)))Run Code Online (Sandbox Code Playgroud)
这可以用作:
user=> (macroexpand-1 '(configure (MyClass.) {:username "fred" :password "wilma"}))
(clojure.core/doto (MyClass.) (.setUsername "fred") (.setPassword "wilma"))Run Code Online (Sandbox Code Playgroud)