La mayoría de los principales éxitos de Google para "llamar a clojure desde java" están desactualizados y recomiendan usarlos clojure.lang.RT
para compilar el código fuente. ¿Podría ayudarnos con una explicación clara de cómo llamar a Clojure desde Java suponiendo que ya ha creado un jar desde el proyecto Clojure y lo ha incluido en el classpath?
java
clojure
clojure-java-interop
Arthur Ulfeldt
fuente
fuente
Respuestas:
Actualización : desde que se publicó esta respuesta, algunas de las herramientas disponibles han cambiado. Después de la respuesta original, hay una actualización que incluye información sobre cómo construir el ejemplo con las herramientas actuales.
No es tan simple como compilar en un jar y llamar a los métodos internos. Sin embargo, parece haber algunos trucos para que todo funcione. Aquí hay un ejemplo de un archivo Clojure simple que se puede compilar en un jar:
Si lo ejecuta, debería ver algo como:
Y aquí hay un programa Java que llama a la
-binomial
función en eltiny.jar
.Su salida es:
La primera pieza de magia es usar la
:methods
palabra clave en lagen-class
declaración. Eso parece ser necesario para permitirle acceder a la función Clojure, algo así como métodos estáticos en Java.Lo segundo es crear una función de contenedor que Java pueda invocar. Observe que la segunda versión de
-binomial
tiene un guión delante.Y, por supuesto, el frasco de Clojure debe estar en el camino de la clase. Este ejemplo utiliza el jar Clojure-1.1.0.
Actualización : esta respuesta se ha vuelto a probar con las siguientes herramientas:
La parte de Clojure
Primero cree un proyecto y una estructura de directorios asociada usando Leiningen:
Ahora, cambie al directorio del proyecto.
En el directorio del proyecto, abra el
project.clj
archivo y edítelo de modo que el contenido sea el que se muestra a continuación.Ahora, asegúrese de que todas las dependencias (Clojure) estén disponibles.
Es posible que vea un mensaje sobre la descarga del jar Clojure en este momento.
Ahora edite el archivo Clojure de
C:\projects\com.domain.tiny\src\com\domain\tiny.clj
modo que contenga el programa Clojure que se muestra en la respuesta original. (Este archivo se creó cuando Leiningen creó el proyecto).Gran parte de la magia aquí está en la declaración del espacio de nombres. El
:gen-class
le dice al sistema que cree una clase nombradacom.domain.tiny
con un único método estático llamadobinomial
, una función que toma dos argumentos enteros y devuelve un doble. Hay dos funciones con nombres similaresbinomial
, una función Clojure tradicional-binomial
y un contenedor accesible desde Java. Tenga en cuenta el guión en el nombre de la función-binomial
. El prefijo predeterminado es un guión, pero se puede cambiar a otra cosa si lo desea. La-main
función solo hace un par de llamadas a la función binomial para garantizar que estamos obteniendo los resultados correctos. Para hacer eso, compila la clase y ejecuta el programa.Debería ver la salida que se muestra en la respuesta original.
Ahora empaquételo en un frasco y colóquelo en un lugar conveniente. Copie el frasco de Clojure allí también.
La parte de Java
Leiningen tiene una tarea incorporada
lein-javac
, que debería poder ayudar con la compilación de Java. Desafortunadamente, parece estar roto en la versión 2.1.3. No puede encontrar el JDK instalado y no puede encontrar el repositorio de Maven. Los caminos a ambos tienen espacios incrustados en mi sistema. Supongo que ese es el problema. Cualquier IDE de Java también podría manejar la compilación y el empaquetado. Pero para esta publicación, vamos a la vieja escuela y lo hacemos en la línea de comando.Primero cree el archivo
Main.java
con el contenido que se muestra en la respuesta original.Para compilar parte de Java
Ahora cree un archivo con alguna metainformación para agregar al jar que queremos construir. En
Manifest.txt
, agregue el siguiente textoAhora empaquete todo en un gran archivo jar, incluido nuestro programa Clojure y el jar Clojure.
Para ejecutar el programa:
La salida es esencialmente idéntica a la producida por Clojure solo, pero el resultado se ha convertido en un Java doble.
Como se mencionó, un IDE de Java probablemente se encargará de los argumentos de compilación desordenados y el empaquetado.
fuente
A partir de Clojure 1.6.0, existe una nueva forma preferida de cargar e invocar funciones de Clojure. Ahora se prefiere este método a llamar a RT directamente (y reemplaza muchas de las otras respuestas aquí). El javadoc está aquí , el punto de entrada principal es
clojure.java.api.Clojure
.Para buscar y llamar a una función Clojure:
Las funciones en
clojure.core
se cargan automáticamente. Se pueden cargar otros espacios de nombres mediante require:IFn
s se puede pasar a funciones de orden superior, por ejemplo, el siguiente ejemplo pasaplus
aread
:La mayoría de los
IFn
s en Clojure se refieren a funciones. Algunos, sin embargo, se refieren a valores de datos que no funcionan. Para acceder a estos, use enderef
lugar defn
:A veces (si usa alguna otra parte del tiempo de ejecución de Clojure), es posible que deba asegurarse de que el tiempo de ejecución de Clojure se inicialice correctamente; llamar a un método en la clase Clojure es suficiente para este propósito. Si no necesita llamar a un método en Clojure, simplemente hacer que la clase se cargue es suficiente (en el pasado ha habido una recomendación similar para cargar la clase RT; ahora se prefiere):
fuente
Clojure.read("'(1 2 3")
. Sin embargo, sería razonable presentar esto como una solicitud de mejora para proporcionar un Clojure.quote () o haciéndolo funcionar como una var.'(1 2 3)
, escriba algo comoClojure.var("clojure.core", "list").invoke(1,2,3)
. Y la respuesta ya tiene un ejemplo de cómo usarlarequire
: es solo una var como cualquier otra.EDITAR Esta respuesta fue escrita en 2010 y funcionó en ese momento. Vea la respuesta de Alex Miller para una solución más moderna.
¿Qué tipo de código están llamando desde Java? Si tiene una clase generada con gen-class, simplemente llámela. Si desea llamar a la función desde el script, consulte el siguiente ejemplo .
Si desea evaluar el código de una cadena, dentro de Java, puede usar el siguiente código:
fuente
RT.var("namespace", "my-var").deref()
.RT.load("clojure/core");
al principio. ¿Qué comportamiento extraño?EDITAR: escribí esta respuesta hace casi tres años. En Clojure 1.6 hay una API adecuada exactamente para llamar a Clojure desde Java. Por favor, la respuesta de Alex Miller para obtener información actualizada.
Respuesta original de 2011:
Según lo veo, la forma más simple (si no genera una clase con la compilación AOT) es usar clojure.lang.RT para acceder a las funciones en clojure. Con él puedes imitar lo que habrías hecho en Clojure (no es necesario compilar cosas de maneras especiales):
Y en Java:
Es un poco más detallado en Java, pero espero que esté claro que las piezas de código son equivalentes.
Esto debería funcionar siempre que Clojure y los archivos fuente (o archivos compilados) de su código Clojure estén en el classpath.
fuente
Estoy de acuerdo con la respuesta de clartaq, pero sentí que los principiantes también podrían usar:
Así que cubrí todo eso en esta publicación de blog .
El código Clojure se ve así:
La configuración del proyecto leiningen 1.7.1 se ve así:
El código Java se ve así:
O también puede obtener todo el código de este proyecto en github .
fuente
Esto funciona con Clojure 1.5.0:
fuente
Si el caso de uso es incluir un JAR construido con Clojure en una aplicación Java, he encontrado que tener un espacio de nombres separado para la interfaz entre los dos mundos es beneficioso:
El espacio de nombres principal puede usar la instancia inyectada para realizar sus tareas:
Para fines de prueba, la interfaz se puede tropezar:
fuente
Otra técnica que funciona también con otros idiomas además de JVM es declarar una interfaz para las funciones que desea llamar y luego usar la función 'proxy' para crear una instancia que las implemente.
fuente
También puede usar la compilación AOT para crear archivos de clase que representen su código de clojure. Lea la documentación sobre compilación, gen-class y amigos en los documentos de la API de Clojure para obtener detalles sobre cómo hacer esto, pero en esencia creará una clase que llama a las funciones de clojure para cada invocación de método.
Otra alternativa es utilizar la nueva funcionalidad defprotocol y deftype, que también requerirá compilación AOT pero proporcionará un mejor rendimiento. Todavía no conozco los detalles de cómo hacer esto, pero una pregunta en la lista de correo probablemente funcionaría.
fuente