Here's an extract from a REPL session that hopefully explains what I want to achieve:
user> (Integer/parseInt "1")
1
user> (def y Integer)
#'user/y
user> (y/parseInt "1")
No such namespace: y
[Thrown class java.lang.Exception]
How can I access static method/fields of a Java class using a non-Classname, user defined symbol?
UPDATE
The following works as expected:
user> (eval (list (symbol (.getName y) "parseInt") "1"))
1
Is there a better/more idiomatic way to achieve the same result?
If you cannot determine the class (possibly programmatically in a macro) during compile time, you need to resort to use reflection. This would do the same thing as eval does when it tries to compile the code. See 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"]))
This can be used in arbitrary ways at runtime, since it's not a macro or a special form. For example, the name of the method could be given by the user via a GUI or though a socket at runtime.
If you have the name of the class at compile time, you can use a macro as Nicolas suggested. However, it's unnecessary to construct the code to look like (Integer/parseInt "1")
, since it's just syntactic sugar for the more basic (and macro friendly) .
special form: (. 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))
However, the only "real work" this macro performs is to convert the string into a symbol. You would probably just use the .
special form in an outer macro (the one that acquires the names of the methods somehow, e.g. by getting those passed as arguments, or by reading them from a var or from a configuration file).
;; 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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With