我该如何创建一个实现此接口的Clojure对象,并且让它可以从Java代码中调用?
public interface Doer {
public String doSomethin(String input);
}
Doer clojureDoer = ?;
String output = clojureDoer.doSomethin(input);
reify
更适合用来实现接口 - proxy
过于笨重、老旧和缓慢,所以应尽可能避免使用。实现的代码如下:
(reify Doer
(doSomethin [this input]
(...whatever...)))
截至 Clojure 1.6 版本,推荐的方法如下所示。假设您的类路径中包含 Clojure 1.6 的 jar 文件和以下 clojure 文件(或其已编译等效物):
(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))))
那么,从Java中,您可以通过以下方式访问它:
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");
}
}
参见proxy
宏。 Clojure文档中有一些示例。此外,Java互操作页面也涵盖了此内容。
(proxy [Doer] []
(doSomethin [input]
(str input " went through proxy")))
proxy
返回一个实现 Doer
接口的对象。若要从 Java 中访问该对象,您需要使用 gen-class
使您的 Clojure 代码可从 Java 调用。这在回答 "Calling clojure from java"问题中有所涉及。
(ns doer-clj
(:gen-class
:name DoerClj
:implements [Doer]
:methods [[doSomethin [String] String]]))
(defn -doSomethin
[_ input]
(str input " went through Clojure"))
现在将其保存为doer_clj.clj
,mkdir classes
并通过在你的REPL中调用(require 'doer-clj) (compile 'doer-clj)
进行编译。您应该会发现DoerClj.class
已经准备好在classes
目录中供Java使用。
Runnable
接口的这个简单示例。当我(require 'doer-clj) (compile 'doer-clj)
时,我可以毫无问题地调用DoerClj.run()
。 - Jan对于这个问题的更普遍的看法,当你需要某种Java-interop时,这个图表可能非常有用:
https://github.com/cemerick/clojure-type-selection-flowchart
doSomethin()
,那么在:methods
中就不应该提及它。引用自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.