How to invoke java method obtained using clojure.reflect

1,130 views
Skip to first unread message

Pablo Nussembaum

unread,
Feb 11, 2013, 3:34:00 PM2/11/13
to clo...@googlegroups.com
Hey all,
I'm a newbie that is trying to use clojure for my university grade thesis.
In our project we have to generate a graph structure invoking some methods on a standard java class. This class is provided at run time, so the methods that need to be called must be obtained by
reflection. I've been trying to use clojure.reflect, but I was not able to find a way to invoke a method on the class with the symbols retrieved from reflect (I know that I can use java interop and
use standard java reflection api).

Here is and example of what I want to do:
user=> (require '[clojure.reflect :as r])
user=> (def method-def (filter #(= "toString" (str (:name %))) (:members (r/reflect "aClass"))))

How do I invoke "toString" using "method-definition"?

Thanks and regards,
--
Pablo Nussembaum



what is better to use java interop using java reflection API or use clojure.reflect to option

juan.facorro

unread,
Feb 11, 2013, 7:53:02 PM2/11/13
to clo...@googlegroups.com
Since a macro's arguments must be available at compile time, creating one for calling a runtime defined method won't work.

Because of this we are left with eval, which actually compiles and then evaluates at runtime the expression it receives as an argument.

Here's a function that receives an object, a method name (as a symbol or string) and optional arguments; and then uses eval to find the result:


(ns calling-methods
(:require [clojure.reflect :as r]))
;-----------------------------
(defn call-method* [obj m & args]
(eval `(. ~obj ~(symbol m) ~@args)))
;-----------------------------
(def method-def (->> (r/reflect "String")
:members
(filter #(= "toString" (str (:name %))))
first))
 
(let [x {:a 1 :b 2}
s "hi"
upper "toUpperCase"]
(println (call-method* x (:name method-def)))
(println (call-method* "hi" (:name method-def)))
(println (call-method* "hi" upper)))

Hope it helps.

J

AtKaaZ

unread,
Feb 12, 2013, 7:21:20 AM2/12/13
to clo...@googlegroups.com
seems similar to this concept with "new":

=> (new java.lang.RuntimeException "msg") ;works this way
#<RuntimeException java.lang.RuntimeException: msg>

=> (def a java.lang.RuntimeException)
#'runtime.q/a
=> a
java.lang.RuntimeException
=> (new a "msg") ;nope
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: a, compiling:(NO_SOURCE_PATH:1:1)
=> (new (eval a) "msg") ; nope
CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: (eval a), compiling:(NO_SOURCE_PATH:1:2)

=> (defmacro new-instance [cls & args]
     `(new ~(eval cls) ~@args))
#'runtime.q/new-instance
=> (new-instance a "msg") ; works
#<RuntimeException java.lang.RuntimeException: msg>

=> (new-instance 'java.lang.RuntimeException "msg") ;also works
#<RuntimeException java.lang.RuntimeException: msg>


or with "."
=> (defmacro call-method [inst m & args]
     `(. ~inst ~(symbol (eval m)) ~@args))

#'runtime.q/call-method
=> (def someInst "somestringinstance")
#'runtime.q/someInst
=> (call-method someInst 'toUpperCase)
"SOMESTRINGINSTANCE"
=> (call-method someInst "toUpperCase")
"SOMESTRINGINSTANCE"
=> (call-method someInst toUpperCase)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: toUpperCase in this context, compiling:(NO_SOURCE_PATH:1:1)
=> (def four (* 2 2))
#'runtime.q/four
=> (call-method someInst 'substring (+ 1 1) four)
"me"
=> (call-method someInst "substring" (+ 1 1) four)
"me"
=> (call-method "abcde" (quote substring) 2 four)
"cd"



--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Please correct me if I'm wrong or incomplete,
even if you think I'll subconsciously hate it.

Meikel Brandmeyer (kotarak)

unread,
Feb 12, 2013, 7:51:01 AM2/12/13
to clo...@googlegroups.com
Hi,

if you want to resort to eval you can define your own function on the fly.

(defn call-fn
  [& args]
  {:arglists ([class method & args])}
  (let [o (gensym)
        [class method & args] (map symbol args)]
    (eval
      `(fn [~o ~@args]
         (. ~(with-meta o {:tag class})
            (~method ~@args))))))

user=> (set! *warn-on-reflection* true)
true
user=> (def f (call-fn "java.io.File" "renameTo" "dest"))
#'user/f
user=> (f (java.io.File. "foo") (java.io.File. "bar"))
false

Kind regards
Meikel

AtKaaZ

unread,
Feb 12, 2013, 8:38:45 AM2/12/13
to clo...@googlegroups.com
wow that's pretty epic!

=> (def f (call-fn "java.lang.String" "substring" "startpos" "endpos"))
#'runtime.q/f
=> (f "abcdef" 2 4)
"cd"
=> (f (str "123" "45") (+ 1 1) 4)
"34"



Thank you Meikel


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Meikel Brandmeyer (kotarak)

unread,
Feb 12, 2013, 8:45:44 AM2/12/13
to clo...@googlegroups.com
You can also do away with the argument names. You just need the number of arguments.

(defn call-fn
  [class method n-args]
  (let [o    (gensym)
        args (repeatedly n-args gensym)
        [class method] (map symbol [class method])]
    (eval
      `(fn [~o ~@args]
         (. ~(with-meta o {:tag class})
            (~method ~@args))))))

user=> (def f (call-fn "java.io.File" "renameTo" 1))
#'user/f
user=> (f (java.io.File. "foo") (java.io.File. "bar"))
false

Meikel

juan.facorro

unread,
Feb 12, 2013, 9:16:44 AM2/12/13
to clo...@googlegroups.com
Awesome :)

Pablo Nussembaum

unread,
Feb 12, 2013, 5:09:20 PM2/12/13
to clo...@googlegroups.com
Hey,
I really appreciate your help guys. This is very useful.

Aaron Cohen

unread,
Feb 12, 2013, 5:12:55 PM2/12/13
to clo...@googlegroups.com
You're actually probably better off using clojure's reflector (clojure.lang.Reflector/invokeStaticMethod ...) or (clojure.lang.Reflector/invokeInstanceMethod ...)

That way you get type coercions that match clojure's behaviour.


Awesome :)

zcaudate

unread,
Feb 13, 2014, 6:32:20 AM2/13/14
to clo...@googlegroups.com
Try my new library. It makes reflection really easy to use
http://github.com/zcaudate/iroh
Reply all
Reply to author
Forward
0 new messages