Как реализовать интерфейс Java в Clojure - PullRequest
17 голосов
/ 23 декабря 2011

Как мне создать объект Clojure, который реализует этот интерфейс и затем вызывается из кода Java?

public interface Doer {
   public String doSomethin(String input);
}

Doer clojureDoer = ?;

String output = clojureDoer.doSomethin(input);

Ответы [ 5 ]

43 голосов
/ 23 декабря 2011

reify настоятельно рекомендуется для реализации интерфейсов - proxy является тяжелым, старым и медленным, поэтому его следует по возможности избегать.Реализация будет выглядеть следующим образом:

(reify Doer
  (doSomethin [this input]
    (...whatever...)))

Обратите внимание, что существующий ответ об использовании proxy имеет неправильный синтаксис, если вы все-таки решите использовать прокси: прокси принимает неявный аргумент this, а неименованный первый аргумент.

14 голосов
/ 11 мая 2014

Начиная с Clojure 1.6 , предпочтительный подход будет следующим.Предполагая, что на вашем пути к классам есть jar-файл 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");
    }
}
12 голосов
/ 23 декабря 2011

с прокси

См. Макрос proxy. Документы Clojure имеют несколько примеров. Он также описан на странице Java Interop .

(proxy [Doer] []
  (doSomethin [input]
    (str input " went through proxy")))

proxy возвращает объект, реализующий Doer. Теперь, чтобы получить доступ к нему в Java, вы должны использовать gen-class, чтобы ваш код Clojure мог вызываться из Java. Он описан в ответе на вопрос «Вызов clojure из 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 готовый к использованию из Java в каталоге classes

8 голосов
/ 23 декабря 2011

Для более общего понимания этого вопроса эта диаграмма может быть очень полезна, когда вам нужно какое-то взаимодействие с Java:

https://github.com/cemerick/clojure-type-selection-flowchart

0 голосов
/ 30 декабря 2013

Если в вашем интерфейсе определено 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...