2011-12-23 12 views
15

¿Cómo creo un objeto Clojure que implementa esta interfaz y luego se llama desde código Java?¿Cómo implemento una interfaz Java en Clojure

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

Doer clojureDoer = ?; 

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

gracias ¡Todos por ayudarme! Terminé usando Reify y simplemente escribí la función principal en clojure. ¡Clojure es increíblemente genial! –

Respuesta

41

reify es preferido para la implementación de interfaces - proxy es pesado, viejo y lento, por lo que debe evitarse cuando sea posible. Una implementación se vería así:

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

Tenga en cuenta que la respuesta ya existente sobre el uso de proxy tiene una sintaxis incorrecta, si usted decide ir con un proxy, después de todo: de proxy toma un argumento implícito this, no un primer argumento con nombre.

+0

Cool. ¿Es posible llamar al objeto clojureDoer desde Java? –

+4

Gracias por señalar nuestro error en mi respuesta. Sin embargo, sería mucho más útil si lo agregaras como comentario en mi respuesta. ¡Aclamaciones! – Jan

12

Con Proxy

Véase el proxy macro. Clojure Docs tienen algunos ejemplos. También está cubierto en la página Java Interop.

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

proxy devuelve un objeto que implementa Doer. Ahora, para acceder a él en Java, debe usar gen-class para hacer que su código Clojure pueda llamarse desde Java. Está cubierto en una respuesta a la pregunta "Calling clojure from java".

Con Gen clase

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

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

Ahora guárdelo como doer_clj.clj, mkdir classes y compilarlo llamando en su REPL (require 'doer-clj) (compile 'doer-clj). Usted debe encontrar DoerClj.class listo para ser utilizado a partir de Java en el directorio classes

+0

Nota "Doer clojureDoer =?" en mi pregunta. ¿Qué pongo en "?" para hacer que el programa funcione El enlace que le muestra muestra cómo importar un objeto clojure como una clase estática. ¡Gracias! –

+0

En tal caso, es posible que esté más interesado en la segunda mitad de mi respuesta que acabo de agregar. Está libre de "proxy" y parece mucho más adecuado para su caso. Creo que eliminaré la primera mitad si la segunda soluciona tu problema. – Jan

+0

Interesante .. Tuve que poner Doer en un paquete, porque el compilador estaba buscando java.lang.Doer de lo contrario. Obtuve una excepción de excepción en el hilo "principal" java.lang.ClassFormatError: Duplicate method name & signature en el archivo de clase DoerClj cuando hago Doer doer = new DoerClj(); –

7

Para una toma más general sobre esta cuestión, este diagrama se puede FREAKING útil cuando usted está en necesidad de algún tipo de Java interoperabilidad:

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

+0

Me encontré con eso una vez, pero no me di cuenta de qué tan bueno es realmente un recurso. Gracias por señalar eso. – Bill

0

Si doSomethin() se define en su interfaz, debería no mencionarlo en :methods. Presupuesto de 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

Como de Clojure 1.6, el enfoque preferido sería como sigue. Asumiendo que tiene, en su ruta de clases, el frasco Clojure 1.6 y el siguiente archivo de clojure (o su equivalente compilado):

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

entonces, desde Java, se puede acceder a ella de la siguiente manera:

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

Gracias @Gary Verhaegen;) –

Cuestiones relacionadas