java – 从非Classname符号访问类的静态字段

以下是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)
点赞