以下是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类的静态方法/字段?
更新
以下按预期方式工作:
user> (eval (list (symbol (.getName y) "parseInt") "1"))
1
是否有更好/更惯用的方式来达到相同的效果?
答案 0 :(得分:2)
如果在编译期间无法确定类(可能在宏中以编程方式),则需要求助于使用反射。这与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")
,因为它只是更基本(和宏友好).
特殊形式的语法糖:(. Integer 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)
答案 1 :(得分:0)
理论上,以下方法可行:
你可以编写一个允许你做的宏def-alias(def-alias y Integer)。这个宏应该:
这有点难看,因为这种方法也会为你不需要的方法创建包装器。
无保证;)
答案 2 :(得分:0)
我认为没有比你提供的eval
电话更好的方式了。你总是可以将它包装在一个漂亮的宏中:
(defmacro static-call [var method & args]
`(-> (.getName ~var)
(symbol ~(str method))
(list ~@args)
eval))
更新:根据raek的建议,这是使用Reflector
类的版本:
(defmacro static-call [var method & args]
`(clojure.lang.Reflector/invokeStaticMethod
(.getName ~var)
~(str method)
(to-array ~(vec args))))
请注意,我在两种情况下都编写了一个宏,只是为了方便保存一些字符。为了获得更好的性能,您应该直接使用invokeStaticMethod
。