2011-12-23 15 views
15

Come si crea un oggetto Clojure che implementa questa interfaccia e viene chiamato dal codice Java?Come implementare un'interfaccia Java in Clojure

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

Doer clojureDoer = ?; 

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

grazie tutti per avermi aiutato! Ho finito per usare Reify e ho appena scritto la funzione principale in clojure. il clojure è incredibilmente interessante! –

risposta

41

reify è fortemente preferito per le interfacce di attuazione - proxy è pesante, vecchio e lento, quindi dovrebbe essere evitato quando possibile. Un'implementazione sarà simile:

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

Nota che la risposta esistente sull'utilizzo proxy ha una sintassi errata, se si decide di andare con un proxy, dopo tutto: procura prende un this argomento implicito, non un primo argomento di nome.

+0

Fresco. È possibile quindi chiamare l'oggetto clojureDoer da Java? –

+4

Grazie per aver segnalato l'errore nella mia risposta. Sarebbe molto più utile se lo aggiungessi come commento sotto la mia risposta, però. Saluti! – Jan

12

con delega

Vedi l'proxy macro. Clojure Docs hanno alcuni esempi. È anche coperto dalla pagina Java Interop.

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

proxy restituisce un oggetto che implementa Doer. Ora, per accedervi in ​​Java, è necessario utilizzare gen-class per rendere il codice Clojure richiamabile da Java. È coperto in una risposta alla domanda "Calling clojure from java".

Con Gen-class

(ns doer-clj 
    (:gen-class 
    :name DoerClj 
    :implements [Doer] 
    :methods [[doSomethin [String] String]])) 

(defn -doSomethin 
    [_ input] 
    (str input " went through Clojure")) 

Ora salvarlo come doer_clj.clj, mkdir classes e compilarlo chiamando nel vostro REPL (require 'doer-clj) (compile 'doer-clj). Si dovrebbe trovare DoerClj.class pronto per essere utilizzato da Java in classes directory

+0

Nota "Doer clojureDoer =?" nella mia domanda Cosa inserisco in "?" per far funzionare il programma. Il collegamento che fornisci mostra come importare un oggetto clojure come una classe statica. Grazie! –

+0

In tal caso potresti essere più interessato alla seconda metà della mia risposta che ho appena aggiunto. È 'proxy'-free e sembra molto più adeguato al tuo caso. Penso che cancellerò il primo tempo se il secondo risolve il tuo problema. – Jan

+0

Interessante .. Ho dovuto inserire Doer in un pacchetto, perché altrimenti il ​​compilatore cercava java.lang.Doer. Ho ottenuto un'eccezione Eccezione nel thread "main" java.lang.ClassFormatError: duplica il nome del metodo e la firma nel file di classe DoerClj quando eseguo Doer doer = new DoerClj(); –

7

Per un introito più generale sulla questione, questo schema può essere impazzendo utile quando si è nel bisogno di un qualche tipo di Java interoperabilità:

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

+0

Mi sono imbattuto in una volta, ma non mi ero reso conto di quanto sia buona una risorsa. Grazie per la segnalazione. – Bill

0

Se doSomethin() è definito nella vostra interfaccia, dovrebbe non menzionarlo in :methods. Citazione da 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

Al Clojure 1.6, l'approccio preferito sarebbe il seguente. Supponendo di avere, sul classpath, il vaso Clojure 1.6 e il seguente file clojure (o il suo equivalente compilato):

(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)))) 

poi, da Java, è possibile accedere come segue:

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

Grazie @Gary Verhaegen;) –