2011-12-23 2 views
15

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

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

Doer clojureDoer = ?; 

String output = clojureDoer.doSomethin(input); 
+0

спасибо все за то, что помогли мне! Я закончил использование Reify и просто написал основную функцию в clojure. clojure невероятно круто! –

ответ

41

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

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

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

+0

Прохладный. Можно ли затем вызвать объект clojureDoer из Java? –

+4

Спасибо, что указали на нашу ошибку в моем ответе. Было бы гораздо полезнее, если бы вы добавили его в качестве комментария под моим ответом. Ура! – Jan

12

С прокси

proxy Смотрите макрос. Clojure Docs есть несколько примеров. Он также рассмотрен на странице Java Interop.

(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 и скомпилировать его по телефону в вашем РЕПЛ (require 'doer-clj) (compile 'doer-clj). Вы должны найти DoerClj.class готовы к использованию из Java в classes каталоге

+0

Примечание «Doer clojureDoer =?» в моем вопросе. Что я вкладываю в "?" чтобы программа работала. Ссылка, которую вы даете, показывает, как импортировать объект clojure в качестве статического класса. Благодаря! –

+0

В таком случае вас может заинтересовать вторая половина моего ответа, которую я только что добавил. Это 'proxy'-free и кажется более адекватным вашему делу. Я думаю, что я удалю первую половину, если вторая решит вашу проблему. – Jan

+0

Интересно .. Мне пришлось положить Doer в пакет, потому что компилятор искал java.lang.Doer в противном случае. У меня есть исключение Исключение в потоке «main» java.lang.ClassFormatError: Дублировать имя и подпись метода в файле класса DoerClj, когда я делаю Doer doer = new DoerClj(); –

7

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

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

+0

Я столкнулся с этим раньше, но я не понял, насколько хорош ресурс, который он на самом деле. Спасибо что подметил это. – Bill

0

Если 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. 
13

По 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"); 
    } 
} 
+0

Спасибо @Gary Verhaegen;) –

Смежные вопросы