Как реализовать интерфейс Java в Clojure
Как создать объект Clojure, который реализует этот интерфейс, а затем вызывается из кода Java?
public interface Doer {
public String doSomethin(String input);
}
Doer clojureDoer = ?;
String output = clojureDoer.doSomethin(input);
Ответы
Ответ 1
reify
настоятельно рекомендуется для реализации интерфейсов - proxy
является сверхпрочным, старым и медленным, поэтому его следует избегать, когда это возможно. Реализация будет выглядеть так:
(reify Doer
(doSomethin [this input]
(...whatever...)))
Обратите внимание, что существующий ответ об использовании proxy
имеет неправильный синтаксис, если вы решите пойти с прокси-сервером: proxy принимает неявный аргумент this
, а не именованный первый аргумент.
Ответ 2
По Clojure 1.6 предпочтительный подход будет следующим. Предполагая, что на пути к классу классов Clojure 1,6 и следующий 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");
}
}
Ответ 3
С прокси
См. макрос proxy
. Clojure Документы имеют несколько примеров. Он также рассмотрен на странице Java Interop.
(proxy [Doer] []
(doSomethin [input]
(str input " went through proxy")))
proxy
возвращает объект, реализующий Doer
. Теперь, чтобы получить доступ к нему в Java, вы должны использовать gen-class
, чтобы сделать код Clojure вызываемым с Java. Он был рассмотрен в ответе на вопрос "Вызов Clojure из java" .
С gen-class
(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
готовый к использованию из Java в каталоге classes
Ответ 4
Для более общего принятия этого вопроса эта диаграмма может быть полезной, когда вы нуждаетесь в каком-то Java-interop:
https://github.com/cemerick/clojure-type-selection-flowchart
Ответ 5
Если в вашем интерфейсе определен 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 metadata. Do not repeat superclass/interface signatures
here.