java - starting - clojure vs scala
¿Cómo implemento una interfaz Java en Clojure? (5)
¿Cómo creo un objeto Clojure que implementa esta interfaz y luego me llama desde el código Java?
public interface Doer {
public String doSomethin(String input);
}
Doer clojureDoer = ?;
String output = clojureDoer.doSomethin(input);
Con proxy
Ver la macro proxy
. Clojure Docs tiene algunos ejemplos. También está cubierto en la página de interoperabilidad de Java .
(proxy [Doer] []
(doSomethin [input]
(str input " went through proxy")))
proxy
devuelve un objeto implementando Doer
. Ahora, para acceder a él en Java, tiene que usar gen-class
para hacer que su código Clojure se pueda llamar desde Java. Está cubierto en una respuesta a la pregunta "Cómo llamar a clojure desde 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"))
Ahora guárdelo como doer_clj.clj
, mkdir classes
y doer_clj.clj
llamando a su REPL (require ''doer-clj) (compile ''doer-clj)
. Debe encontrar DoerClj.class
listo para ser utilizado desde Java en el directorio de classes
A partir de Clojure 1.6 , el enfoque preferido sería el siguiente. Suponiendo que tiene, en su ruta de clase, el tarro de 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))))
luego, desde Java, puedes acceder a él 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");
}
}
Para una visión más general de esta pregunta, este diagrama puede ser extremadamente útil cuando se necesita algún tipo de interoperabilidad de Java:
https://github.com/cemerick/clojure-type-selection-flowchart
Si doSomethin()
está definido en su interfaz, no debe mencionarlo en :methods
. Cita 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.
reify
es reify
preferido para implementar interfaces: el proxy
es de servicio pesado, antiguo y lento, por lo que debe evitarse cuando sea posible. Una implementación se vería como:
(reify Doer
(doSomethin [this input]
(...whatever...)))
Tenga en cuenta que la respuesta existente sobre el uso del proxy
tiene una sintaxis incorrecta, si decide ir con un proxy después de todo: el proxy toma implícitamente this
argumento, no un primer argumento con nombre.