How do I create a Clojure object that implements this interface and then gets called from Java code?
public interface Doer {
public String doSomethin(Stri
See the proxy
macro. Clojure Docs have some examples. It's also covered on Java Interop page.
(proxy [Doer] []
(doSomethin [input]
(str input " went through proxy")))
proxy
returns an object implementing Doer
. Now, to access it in Java you have to use gen-class
to make your Clojure code callable from Java. It's covered in an answer to the "Calling clojure from java" question.
(ns doer-clj
(:gen-class
:name DoerClj
:implements [Doer]
:methods [[doSomethin [String] String]]))
(defn -doSomethin
[_ input]
(str input " went through Clojure"))
Now save it as doer_clj.clj
, mkdir classes
and compile it by calling in your REPL (require 'doer-clj) (compile 'doer-clj)
. You should find DoerClj.class
ready to be used from Java in classes
directory
reify
is strongly preferred for implementing interfaces - proxy
is heavy-duty, old, and slow, so should be avoided when possible. An implementation would look like:
(reify Doer
(doSomethin [this input]
(...whatever...)))
If doSomethin()
is defined in your interface, you should not mention it in :methods
. Quote from http://clojuredocs.org/clojure_core/clojure.core/gen-class:
:methods [ [name [param-types] return-type], ...]
The generated class automatically defines all of the non-private
methods of its superclasses/interfaces. This parameter can be used
to specify the signatures of additional methods of the generated
class. Static methods can be specified with ^{:static true} in the
signature's metadata. Do not repeat superclass/interface signatures
here.
For a more general take on this question, this diagram can be freaking useful when you are in need for some kind of Java-interop:
https://github.com/cemerick/clojure-type-selection-flowchart
As of Clojure 1.6, the preferred approach would be as follows. Assuming you have, on your classpath, the Clojure 1.6 jar and the following clojure file (or its compiled equivalent):
(ns my.clojure.namespace
(:import [my.java.package Doer]))
(defn reify-doer
"Some docstring about what this specific implementation of Doer
does differently than the other ones. For example, this one does
not actually do anything but print the given string to stdout."
[]
(reify
Doer
(doSomethin [this in] (println in))))
then, from Java, you could access it as follows:
package my.other.java.package.or.maybe.the.same.one;
import my.java.package.Doer;
import clojure.lang.IFn;
import clojure.java.api.Clojure;
public class ClojureDoerUser {
// First, we need to instruct the JVM to compile/load our
// Clojure namespace. This should, obviously, only be done once.
static {
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("my.clojure.namespace"));
// Clojure.var() does a somewhat expensive lookup; if we had more than
// one Clojure namespace to load, so as a general rule its result should
// always be saved into a variable.
// The call to Clojure.read is necessary because require expects a Clojure
// Symbol, for which there is no more direct official Clojure API.
}
// We can now lookup the function we want from our Clojure namespace.
private static IFn doerFactory = Clojure.var("my.clojure.namespace", "reify-doer");
// Optionally, we can wrap the doerFactory IFn into a Java wrapper,
// to isolate the rest of the code from our Clojure dependency.
// And from the need to typecast, as IFn.invoke() returns Object.
public static Doer createDoer() {
return (Doer) doerFactory.invoke();
}
public static void main(String[] args) {
Doer doer = (Doer) doerFactory.invoke();
doer.doSomethin("hello, world");
}
}