I have a set of Java classes that all implement the newBuilder interface (they are actually protobuf generated classes). I would like to pass the class as a parameter to a form that returns a function to create a new builder for that class.
(defn create-message-builder
[klass]
(. klass newBuilder))
I cannot get the form dynamically so that it calls the newBuilder static method on klass.
I found a macro on another SO post and made some modifications to support injecting it into my source:
(defmacro jcall [obj & args]
`(let [ref (if (and (symbol? ~obj)
(instance? Class (eval ~obj)))
(eval ~obj)
~obj) ]
(. ref# ~#args)))
When I attempt to call this macro:
repl> (jcall Contact newBuilder)
#object[com.skroot.Contact$Builder 0x5622de90 ""]
I get an error:
IllegalArgumentException No matching field found: newBuilder for class java.lang.Class
The same thing you would do in Java: use reflection to ask the Class object what methods it has, find the one of the right name, and call it with no arguments.
(defn class->builder [c]
(let [m (.getDeclaredMethod c "newBuilder" (into-array Class []))]
(.invoke m nil (into-array Object []))))
Related
My goal is a function/macro that works like this:
(def f (polymorphic-fn
java.lang.Long (fn [a] (inc a))
java.lang.String (fn [a] (str a "x"))))
(f 1) ;; => 2
(f "abc") ;; => "abcx"
Since the type-based dispatch of protocols has the best performance I was thinking to create an 'anonymous' protocol for the 'fused' function with a macro like this:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym)
method (gensym)
extends (for [[type handler] pairs]
`(extend ~type ~proto {(keyword ~method) ~handler}))]
`(do
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
This produces the error: Unable to resolve symbol: G__18707.
Is there a way to return the 'anonymous' method, or is there a better way to implement such a function?
The problem is that the defprotocol will generate code that will intern the protocol methods. Ie. after macroexpansion, the symbols for your defined method is still not known to the compiler. Thus, the compilation fails and will barf that the symbol is unknown.
Many other def...'s will generate a macro "call" that will intern the symbol during macro expansion (and thus the compiler will remain happy).
To fix it you can just declare it beforehand. This works since declare is macro, will get expanded and the compiler will be happy:
(defmacro polymorphic-fn
[& pairs]
(let [proto (gensym "proto")
method (gensym "prot-method-")
extends (for [[type handler] (partition 2 pairs)]
`(extend ~type ~proto {~(keyword (str method)) ~handler}))]
`(do
(declare ~method)
(defprotocol ~proto (~method [e#]))
~#extends
~method)))
Note: I also fixed the keyword call in this.
I think you just want to use regular protocols, along with extend-type:
(defprotocol Fooable
(foo [this]) )
(extend-type java.lang.Long
Fooable
(foo [some-long] (inc some-long)))
(extend-type java.lang.String
Fooable
(foo [any-string] (str any-string "-and-more")))
with result:
(foo 3) => 4
(foo "hello") => "hello-and-more"
It may be possible to use a macro to hide the protocol name by using an auto-gensym, but I don't see the point. Just ignore the protocol name 'Fooable' and you have the same result.
Also, be aware that parts of Clojure implementation create concrete Java classes behind the scenes, which may require a hard-coded name.
You could also mimic the protocol functionality by using a cond:
(defn bar [it]
(cond
(= (class it) java.lang.Long) (inc it)
(= (class it) java.lang.String) (str it "-and-more")))
(bar 7) => 8
(bar "farewell") => "farewell-and-more"
You could define a function to generate bar like you do with polymorphic-fn if you wanted.
Let's say I have come across a class with several static setter methods that I'd like to call in a row. The functionality I'm looking for is similar to that provided by doto, except that it must work on a class instead of an object:
(doto MyClass
(setA "a")
(setB "b"))
Unfortunately, when I try this, I get RuntimeException: Unable to resolve symbol: setA. Is there a special doto macro for classes?
I will leave this question open, since I'm hoping there's a better answer, but here's my home-grown macro to solve this problem:
(defmacro doto-class
"Calls a series of static methods on a class"
[klass & forms]
(cons 'do
(for [f forms]
`(. ~klass ~f))))
which expands the example:
(macroexpand-1
'(doto-class MyClass
(setA "a")
(setB "b")))
to
(do (. MyClass (setA "a"))
(. MyClass (setB "b")))
How do you call a static Java method from Clojure? Like this ...
(Classname/staticMethod args*)
For example,
> (System/getProperty "java.vm.version")
=> "25.141-b15"
We want a macro, say doto-static, that re-arranges this from (doto-static System (getProperty "java.vm.version")), and also allows a chain of calls.
Such a macro is ...
(defmacro doto-static [class-symbol & calls]
(let [qualify (fn [method-symbol] (->> method-symbol
(name)
(str (name class-symbol) \/)
(symbol)))
elaborate (fn [[method-symbol & arg-exprs]]
(cons (qualify method-symbol) arg-exprs))]
(cons 'do (map elaborate calls))))
For example,
> (doto-static System (getProperty "java.vm.version") (getProperty "java.vm.version"))
=> "25.141-b15"
Given the following piece of code:
(map Integer/parseInt ["1" "2" "3" "4"])
Why do I get the following exception unless I wrap Integer/parseInt in an anonymous function and call it manually (#(Integer/parseInt %))?
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer
the documentation on java interop says the following:
The preferred idiomatic forms for accessing field or method members
are given above. The instance member form works for both fields and
methods. The instanceField form is preferred for fields and required
if both a field and a 0-argument method of the same name exist. They
all expand into calls to the dot operator (described below) at
macroexpansion time. The expansions are as follows:
...
(Classname/staticMethod
args*) ==> (. Classname staticMethod args*) Classname/staticField ==>
(. Classname staticField)
so you should remember, that Class/fieldName is just a sugar for getting a static field, neither static method call, nor reference to the static method (java method is not a clojure function really), so there is no static field parseInt in Integer class, while (Class/fieldName arg) calls a static method, they are two totally different operations, using the similar sugary syntax.
so when you do (map #(Integer/parseInt %) ["1" "2" "3" "4"]) it expands to
(map #(. Integer parseInt %) ["1" "2" "3" "4"])
(you can easily see it yourself with macroexpansion),
and (map Integer/parseInt ["1" "2" "3"]) expands to
(map (. Integer parseInt) ["1" "2" "3"])
It fails when it is trying to get a field (which you think is getting a reference to a method).
Integer/parseInt is a static method of Integer class, not a clojure function. Each clojure function is compiled to java class which implements clojure.lang.IFn interface. map expects clojure function (which implements IFn interface) as a first argument, however, Integer/parseInt is not.
You can check that in the clojure repl.
user=> (type map)
clojure.core$map
user=> (type Integer)
java.lang.Class
user=> (type Integer/parseInt)
CompilerException java.lang.RuntimeException: Unable to find static field: parseInt in class java.lang.Integer, compiling:(/private/var/folders/p_/psdvlp_12sdcxq07pp07p_ycs_v5qf/T/form-init4110003279275246905.clj:1:1)
user=> (defn f [] 1)
#'user/f
user=> (type f)
user$f
user=> (type #(1))
user$eval9947$fn__9948
Perhaps reading this stackoverflow question will help you understand what is going on.
You might prefer to do it without the Java interop:
(map read-string ["1" "2"])
or for a more safe variant:
(map clojure.edn/read-string ["1" "2"])
I personally prefer to minimize the use of Java as much as possible in Clojure code.
As for why you can't just pass the Java function, because map expects a function in Clojure, not a function in Java.
I want to use the instance of the class constructed via gen-class in a method of the class.
How do I access it? What do I have insert for "this" in the following example:
(ns example
(:gen-class))
(defn -exampleMethod []
(println (str this)))
Or is it impossible when using gen-class?
The first argument of Clojure functions corresponding to a method generated by gen-class takes the current object whose method is being called.
(defn -exampleMethod [this]
(println (str this)))
In addition to this, you have to add a :methods option to gen-class when you define a method which comes from neither a superclass nor interfaces of the generated class. So a complete example would be as follows.
example.clj
(ns example)
(gen-class
:name com.example.Example
:methods [[exampleMethod [] void]])
(defn- -exampleMethod
[this]
(println (str this)))
REPL
user> (compile 'example)
example
user> (.exampleMethod (com.example.Example.))
com.example.Example#73715410
nil
I'm a few days into learning Clojure and are having some teething problems, so I'm asking for advice.
I'm trying to store a Java class in a Clojure var and call its static methods, but it doesn't work.
Example:
user=> (. java.lang.reflect.Modifier isPrivate 1)
false
user=> (def jmod java.lang.reflect.Modifier)
#'user/jmod
user=> (. jmod isPrivate 1)
java.lang.IllegalArgumentException: No matching method found: isPrivate for class java.lang.Class (NO_SOURCE_FILE:0)
at clojure.lang.Compiler.eval(Compiler.java:4543)
From the exception it looks like the runtime expects a var to hold an object, so it calls .getClass() to get the class and looks up the method using reflection. In this case the var already holds a class, so .getClass() returns java.lang.Class and the method lookup obviously fails.
Is there some way around this, other than writing my own macro?
In the general case I'd like to have either an object or a class in a varible and call the appropriate methods on it - duck typing for static methods as well as for instance methods.
In this specific case I'd just like a shorter name for java.lang.reflect.Modifier, an alias if you wish. I know about import, but looking for something more general, like the Clojure namespace alias but for Java classes. Are there other mechanisms for doing this?
Edit:
Maybe I'm just confused about the calling conventions here. I thought the Lisp (and by extension Clojure) model was to evaluate all arguments and call the first element in the list as a function.
In this case (= jmod java.lang.reflect.Modifier) returns true, and (.getName jmod) and (.getName java.lang.reflect.Modifier) both return the same string.
So the variable and the class name clearly evaluate to the same thing, but they still cannot be called in the same fashion. What's going on here?
Edit 2
Answering my second question (what is happening here), the Clojure doc says that
If the first operand is a symbol that
resolves to a class name, the access
is considered to be to a static member
of the named class... Otherwise it is
presumed to be an instance member
http://clojure.org/java_interop under "The Dot special form"
"Resolving to a class name" is apparently not the same as "evaluating to something that resolves to a class name", so what I am trying to do here is not supported by the dot special form.
(Update: I've prepared something which might be acceptable as a solution... The original answer remains below a horizontal rule towards the end of the post.)
I've just written a macro to enable this:
(adapter-ns java.lang.reflect.Modifier jmod)
; => nil
(jmod/isStatic 1)
; => false
(jmod/isStatic 8)
; => true
The idea is to create a single-purpose namespace, import the statics of a given class as Vars into that namespace, then alias the namespace to some useful name. Convoluted, but it works! :-)
The code is looks like this:
(defmacro import-all-statics
"code stolen from clojure.contrib.import-static/import-static"
[c]
(let [the-class (. Class forName (str c))
static? (fn [x]
(. java.lang.reflect.Modifier
(isStatic (. x (getModifiers)))))
statics (fn [array]
(set (map (memfn getName)
(filter static? array))))
all-fields (statics (. the-class (getFields)))
all-methods (statics (. the-class (getMethods)))
import-field (fn [name]
(list 'def (symbol name)
(list '. c (symbol name))))
import-method (fn [name]
(list 'defmacro (symbol name)
'[& args]
(list 'list ''. (list 'quote c)
(list 'apply 'list
(list 'quote (symbol name))
'args))))]
`(do ~#(map import-field all-fields)
~#(map import-method all-methods))))
(defmacro adapter-ns [c n]
(let [ias (symbol (-> (resolve 'import-all-statics) .ns .name name)
"import-all-statics")]
`(let [ns-sym# (gensym (str "adapter_" ~n))]
(create-ns 'ns-sym#)
(with-ns 'ns-sym#
(clojure.core/refer-clojure)
(~ias ~c))
(alias '~n 'ns-sym#))))
The above looks up the Var holding the import-all-statics macro in a somewhat convoluted way (which is, however, guaranteed to work if the macro is visible from the current namespace). If you know which namespace it's going to be found in, the original version I've written is a simpler replacement:
(defmacro adapter-ns [c n]
`(let [ns-sym# (gensym (str "adapter_" ~n))]
(create-ns 'ns-sym#)
(with-ns 'ns-sym#
(clojure.core/refer-clojure)
;; NB. the "user" namespace is mentioned below;
;; change as appropriate
(user/import-all-statics ~c))
(alias '~n 'ns-sym#)))
(Original answer below.)
I realise that this is not really what you're asking for, but perhaps clojure.contrib.import-static/import-static will be useful to you:
(use 'clojure.contrib.import-static)
(import-static clojure.lang.reflect.Modifier isPrivate)
(isPrivate 1)
; => false
(isPrivate 2)
; => true
Note that import-static imports static methods as macros.
You are successfully storing the class in jmod, but isPrivate is a static method of java.lang.reflect.Modifier, not of java.lang.Class.
You could do this with reflection:
(. (. jmod getMethod "isPrivate" (into-array [Integer/TYPE]))
invoke nil (into-array [1]))
Here is a macro inspired by the two previous answers that handles static methods on class names and variables with class names as well as instance methods on objects:
(defmacro jcall [obj & args]
(let [ref (if (and (symbol? obj)
(instance? Class (eval obj)))
(eval obj)
obj) ]
`(. ~ref ~#args)))
As a relative newbie on macros, the tricky part was getting the evaluation order right.
For other newbies: The obj parameter to the macro is passed in with no evaluation, and we need to force the evaluation of vars so the var name expands into the class name it holds. We need an explicit eval for that, outside the actual macro body.
The test for whether obj is a symbol is there to restrict the evaluation to variables. The test for whether the variable contains a class is there to skip evaluation of non-classes, then it works for objects and instance methods too.
Example use:
;; explicit class name, static method
user=> (jcall java.lang.reflect.Modifier isPrivate 1)
false
;; class name from var, static method
user=> (jcall jmod isPrivate 1)
false
;; works for objects and instance methods too
user=> (jcall (Object.) toString)
"java.lang.Object#3acca07b"
;; even with the object in a variable
user=> (def myobj (Object.))
#'user/myobj
user=> (jcall myobj toString)
"java.lang.Object#4ccbb612"
;; but not for instance methods on classes
user=> (jcall Object toString)
java.lang.NoSuchFieldException: toString (NO_SOURCE_FILE:747)