下面是一个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
是否有更好/更习惯的方法来达到相同的结果?
如果在编译期间无法确定类(可能在宏中以编程方式),则需要使用反射。这将与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)
理论上,以下方法是可行的:
你可以写一个宏define -alias,允许你做(define -alias y Integer)。这个宏应该:
- 定义一个命名空间'y' (create-ns…)
- 查找Integer(或任何其他类)的所有方法,使用(。getMethods…)
- 动态地为命名空间'y'中的所有方法创建瘦包装器
这有点难看,因为这种方法也会为你不需要的方法创建包装器。
不保证;)
我认为没有比您提供的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
。