以下是REPL会话的摘录,希望能够解释我想要实现的目标:
user> (Integer/parseInt "1")
1
user> (def y Integer)
#'user/y
user> (y/parseInt "1")
No such namespace: y
[Thrown class java.lang.Exception]
如何使用非Classname,用户定义的符号访问Java类的静态方法/字段?
UPDATE
以下按预期工作:
user> (eval (list (symbol (.getName y) "parseInt") "1"))
1
是否有更好/更惯用的方法来实现相同的结果?
最佳答案 如果在编译期间无法确定类(可能在宏中以编程方式),则需要求助于使用反射.这与eval在尝试编译代码时所做的一样.请参阅clojure.lang.Reflector / invokeStaticMethod:
https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Reflector.java#L198
(import 'clojure.lang.Reflector)
;; Here, you can pass *any string you have at runtime*
(Reflector/invokeStaticMethod Integer "parseInt" (into-array ["1"]))
这可以在运行时以任意方式使用,因为它不是宏或特殊形式.例如,方法的名称可以由用户通过GUI或在运行时通过套接字给出.
如果您在编译时拥有该类的名称,则可以使用Nicolas建议的宏.但是,没有必要构造看起来像(Integer / parseInt“1”)的代码,因为它只是更基本(和友好的宏)的语法糖.特殊形式:(.整数parseInt“1”).
;; Here, the method name needs to be a *string literal*
(defmacro static-call
"Takes a Class object, a string naming a static method of it
and invokes the static method with the name on the class with
args as the arguments."
[class method & args]
`(. ~class ~(symbol method) ~@args))
但是,此宏执行的唯一“实际工作”是将字符串转换为符号.你可能只是使用.外部宏中的特殊形式(以某种方式获取方法名称的形式,例如通过将它们作为参数传递,或者通过从var或配置文件中读取它们).
;; Use ordinary Clojure functions to construct this
(def the-static-methods {:foo ["Integer" "parseInt"], :bar ["Long" "parseLong"]})
;; Macros have access to all previously defined values
(defmacro generate-defns []
(cons `do (for [[name-keyword [class-string method-string]] the-static-methods]
`(defn ~(symbol (name name-keyword)) [x#]
(. ~(symbol class-string) ~(symbol method-string) x#)))))
(generate-defns)