I'm trying to implement the following Java interface in Clojure:
package quickfix;
public interface MessageFactory {
Message create(String beginString, String msgType);
Group create(String beginString, String msgType, int correspondingFieldID);
}
The following Clojure code is my attempt at doing this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [beginString msgType]
nil)
(create [beginString msgType correspondingFieldID]
nil)))
This fails to compile with the error:
java.lang.IllegalArgumentException: Can't define method not in interfaces: create
The documentation suggests overloaded interface methods are ok, so long as the arity is different as it is in this case:
If a method is overloaded in a protocol/interface, multiple
independent method definitions must be supplied. If overloaded with
same arity in an interface you must specify complete hints to
disambiguate - a missing hint implies Object.
How can I get this working?
You're missing a parameter. The first parameter of every method implemented by reify is the object itself (as is the case with defrecord/deftype). So, try this:
(defn -create-message-factory
[]
(reify quickfix.MessageFactory
(create [this beginString msgType]
nil)
(create [this beginString msgType correspondingFieldID]
nil)))
Related
I am trying to create a new SimpleLinkResolver in Clojure. This is the JavaDoc:
http://prismicio.github.io/java-kit/io/prismic/SimpleLinkResolver.html
My clojure code is:
(def lr (new io.prismic.SimpleLinkResolver))
but at the repl I get the following error:
CompilerException java.lang.InstantiationException, compiling:(form-init460449823042827832.clj:1:1)
I have no problem creating a java.util.Date:
(def d (new java.util.Date))
=> #'prismic-clojure.core/d
d
=> #inst"2018-03-17T10:30:36.016-00:00"
The above JavaDoc does say that SimpleLinkResolver is deprecated because the interface LinkResolver (http://prismicio.github.io/java-kit/io/prismic/LinkResolver.html) has default methods and so can be implemented directly. So I gave this a go to:
(def lr (new io.prismic.LinkResolver))
CompilerException java.lang.IllegalArgumentException: No matching ctor found for interface io.prismic.LinkResolver,
And I get this "no ctor" error - which I am guessing means the compiler can't find a constructor?
Questions:
Why does the first effort produce an InstantiationException?
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
Thanks
Why does the first effort produce an InstantiationException?
You can't instantiate an abstract class:
public abstract class SimpleLinkResolver
Not being familiar with the Java-8 default methods, how would I create a new LinkResolver using its default methods?
You'll need to implement LinkResolver interface, which can be done using Clojure's reify:
(def resolver
(reify LinkResolver
(^String resolve [this ^Fragment$DocumentLink link]
"a string"))) ;; put actual impl. here
(.resolve resolver nil)
;; => "a string"
Note you need to type-hint the return value (and arguments) because .resolve() is an overloaded method.
Also, you typically see (Class.) dot-syntax rather than (new Class) to instantiate Java classes.
How can I use java methods as a functions arguments in Clojure?
For example, I want to make a functions composition:
user> (Integer. (str \9))
9
user> ((comp Integer. str) \9)
CompilerException java.lang.ClassNotFoundException: Integer., compiling:(NO_SOURCE_PATH:1:2)
That does not work.
memfn doesn't help also:
user> (map (comp (memfn Integer.) str) "891")
IllegalArgumentException No matching method found: Integer. for class java.lang.String clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
Any ideas?
Related questions (that, though, do not give the right answer to the question):
Using interop constructor in map function(Clojure)
Why does Clojure say "no matching method" for an illegal argument?
How do I use Clojure memfn with a Java constructor?
How to get an Fn which calls a Java method? (has a nice explanation in the answers)
(Note: it seems to be that the answer suggested by dave, using of an anonymous function as a wrapper, is the best solution)
Unlike Clojure functions, Java methods weren't designed to be first class. When you use Java inter-op in Clojure, you're literally working with Java methods, so you don't get the added benefits that were implemented for Clojure functions. For more info, see the comments below.
As a workaround to use Java methods as arguments, you can wrap them in an anonymous function, like this, effectively making them Clojure functions:
((comp #(Integer. %) str) \9)
I have a method on an object.
myObject.myMethod(1)
I can invoke this in Clojure
(.myMethod myObject 1)
I can also invoke it using information from the lexical environment
(let [x 1] (.myMethod myObject x))
Can I do this with a partial? E.g.
(let [myPartial (partial .myMethod myObject)]
(myPartial 1))
This gives me a
java.lang.RuntimeException: Unable to resolve symbol: .myMethod in this context
I'm currently making this work with an anonymous function
(let [myThing #(.myMethod myObject %)]
(myThing 1))
But if it would be nice to use a partial in this case. Is it possible?
I'm sure the answer will be to do with binding and dispatch but I don't yet have a feeling for where during the compiling and execution the dispatch happens.
You can have partial in your case, use (memfn).
(memfn myMethod args)
In the REPL:
user=> (doc memfn)
-------------------------
clojure.core/memfn
([name & args])
Macro
Expands into code that creates a fn that expects to be passed an
object and any args and calls the named instance method on the
object passing the args. Use when you want to treat a Java method as
a first-class fn. name may be type-hinted with the method receiver's
type in order to avoid reflective calls.
I need to create a class which implements an interface and also has an annotation, and later pass an instance of this class to some API implemented in Java.
With Clojure, I could reify the interface and get an instance like this:
(reify MyInterface (method1 [this] ...))
However, reify doesn't seem to accept annotations. How may I work around this?
First of all reify returns an object not a class. If you need to generate a class with annotations, you will need to use gen-class as shown below (the example shows adding a Deprecated annotation to the class):
(gen-class :name ^{java.lang.Deprecated true} MyClass
...)
Also, you can use deftype:
(deftype ^{java.lang.Deprecated true} MyClass
...)
I don't really understand how the bean function works when I am using it on beans. This code here throws an exception:
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn__5177$fn__5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html
You must have missed it, it's right there.
getName() Returns the name representing the runn
(import java.lang.management.ManagementFactory)
(def runtime (bean (ManagementFactory/getRuntimeMXBean))
(:name runtime)
;; =>
Class clojure.core$bean$fn_5177$fn_5178 can not access a member of class sun.management.RuntimeImpl with modifiers "public"
[Thrown class java.lang.IllegalAccessException]
but the class does have a method called getName(). http://docs.oracle.com/javase/6/docs/api/java/lang/management/RuntimeMXBean.html
ing Java virtual machine.
Edit1:
This issue is very similar to one here and response to it is here.
Class RuntimeImpl returned by ManagementFactory.getRuntimeMXBean is only package visible but it implements public visible interface RuntimeMXBean. So what happens is that function bean tries to call a method on the class RuntimImpl but it can't as class is only package visible. I think if it tried to call getMethod on interface RuntimeMXBean it would have worked.
Yeap this works:
(def mx-bean (ManagementFactory/getRuntimeMXBean))
(def interface-method
(.getMethod RuntimeMXBean "getName" (into-array java.lang.Class [])))
(.invoke interface-method mx-bean (into-array []))
I'm not sure if it's a bug or feature. I would recommend asking on Clojure mailing list.
I think this is captured by a bug right here: http://dev.clojure.org/jira/browse/CLJ-978?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
Upvote it!
You can roll-your-own bean function using the available patch.