La plupart des meilleurs hits Google pour "appeler clojure depuis Java" sont obsolètes et recommandent de les utiliser clojure.lang.RT
pour compiler le code source. Pourriez-vous aider avec une explication claire de la façon d'appeler Clojure à partir de Java en supposant que vous avez déjà construit un fichier jar à partir du projet Clojure et que vous l'avez inclus dans le classpath?
java
clojure
clojure-java-interop
Arthur Ulfeldt
la source
la source
Réponses:
Mise à jour : Depuis que cette réponse a été publiée, certains des outils disponibles ont changé. Après la réponse d'origine, il y a une mise à jour comprenant des informations sur la façon de construire l'exemple avec les outils actuels.
Ce n'est pas aussi simple que de compiler dans un fichier jar et d'appeler les méthodes internes. Il semble cependant y avoir quelques astuces pour que tout fonctionne. Voici un exemple de fichier Clojure simple qui peut être compilé dans un fichier jar:
Si vous l'exécutez, vous devriez voir quelque chose comme:
Et voici un programme Java qui appelle la
-binomial
fonction dans letiny.jar
.Sa sortie est:
Le premier élément de magie consiste à utiliser le
:methods
mot - clé dans lagen-class
déclaration. Cela semble nécessaire pour vous permettre d'accéder à la fonction Clojure quelque chose comme des méthodes statiques en Java.La deuxième chose est de créer une fonction wrapper qui peut être appelée par Java. Notez que la deuxième version de
-binomial
a un tiret devant.Et bien sûr, le pot Clojure lui-même doit être sur le chemin des classes. Cet exemple utilise le jar Clojure-1.1.0.
Mise à jour : cette réponse a été retestée à l'aide des outils suivants:
La partie Clojure
Créez d'abord un projet et la structure de répertoires associée à l'aide de Leiningen:
Maintenant, passez au répertoire du projet.
Dans le répertoire du projet, ouvrez le
project.clj
fichier et modifiez-le de sorte que le contenu soit comme indiqué ci-dessous.Maintenant, assurez-vous que toutes les dépendances (Clojure) sont disponibles.
Vous pouvez voir un message sur le téléchargement du bocal Clojure à ce stade.
Modifiez maintenant le fichier Clojure de
C:\projects\com.domain.tiny\src\com\domain\tiny.clj
sorte qu'il contienne le programme Clojure indiqué dans la réponse d'origine. (Ce fichier a été créé lorsque Leiningen a créé le projet.)Une grande partie de la magie ici est dans la déclaration d'espace de noms. Le
:gen-class
dit au système de créer une classe nomméecom.domain.tiny
avec une seule méthode statique appeléebinomial
, une fonction prenant deux arguments entiers et retournant un double. Il existe deux fonctions nommées de manière similairebinomial
, une fonction Clojure traditionnelle-binomial
et un wrapper accessible depuis Java. Notez le trait d'union dans le nom de la fonction-binomial
. Le préfixe par défaut est un trait d'union, mais il peut être changé en autre chose si vous le souhaitez. La-main
fonction fait juste quelques appels à la fonction binomiale pour s'assurer que nous obtenons les bons résultats. Pour ce faire, compilez la classe et exécutez le programme.Vous devriez voir la sortie affichée dans la réponse originale.
Maintenant, emballez-le dans un bocal et placez-le dans un endroit pratique. Copiez le pot Clojure là aussi.
La partie Java
Leiningen a une tâche intégrée
lein-javac
, qui devrait pouvoir aider à la compilation Java. Malheureusement, il semble être cassé dans la version 2.1.3. Il ne peut pas trouver le JDK installé et il ne peut pas trouver le référentiel Maven. Les chemins vers les deux ont des espaces intégrés sur mon système. Je suppose que c'est le problème. Tout IDE Java peut également gérer la compilation et le packaging. Mais pour ce post, nous allons à l'ancienne et le faisons en ligne de commande.Créez d'abord le fichier
Main.java
avec le contenu affiché dans la réponse d'origine.Pour compiler la partie Java
Créez maintenant un fichier avec des méta-informations à ajouter au fichier jar que nous voulons construire. Dans
Manifest.txt
, ajoutez le texte suivantMaintenant, regroupez tout dans un gros fichier jar, y compris notre programme Clojure et le fichier jar Clojure.
Pour exécuter le programme:
La sortie est essentiellement identique à celle produite par Clojure seul, mais le résultat a été converti en un double Java.
Comme mentionné, un IDE Java s'occupera probablement des arguments de compilation désordonnés et de l'empaquetage.
la source
Depuis Clojure 1.6.0, il existe une nouvelle manière préférée de charger et d'appeler les fonctions Clojure. Cette méthode est maintenant préférée à l'appel direct de RT (et remplace la plupart des autres réponses ici). Le javadoc est ici - le point d'entrée principal est
clojure.java.api.Clojure
.Pour rechercher et appeler une fonction Clojure:
Les fonctions dans
clojure.core
sont automatiquement chargées. D'autres espaces de noms peuvent être chargés via require:IFn
s peut être passé à des fonctions d'ordre supérieur, par exemple, l'exemple ci-dessous passeplus
àread
:La plupart des
IFn
s dans Clojure font référence à des fonctions. Quelques-uns, cependant, font référence à des valeurs de données non fonctionnelles. Pour y accéder, utilisez à laderef
place defn
:Parfois (si vous utilisez une autre partie du runtime Clojure), vous devrez peut-être vous assurer que le runtime Clojure est correctement initialisé - appeler une méthode sur la classe Clojure est suffisant à cet effet. Si vous n'avez pas besoin d'appeler une méthode sur Clojure, il suffit alors de simplement charger la classe (dans le passé, il y avait une recommandation similaire pour charger la classe RT; c'est maintenant préféré):
la source
Clojure.read("'(1 2 3")
. Il serait raisonnable de déposer ceci comme une demande d'amélioration mais pour fournir un Clojure.quote () ou en le faisant fonctionner comme un var.'(1 2 3)
écrivez quelque chose commeClojure.var("clojure.core", "list").invoke(1,2,3)
. Et la réponse a déjà un exemple d'utilisationrequire
: c'est juste une var comme les autres.EDIT Cette réponse a été écrite en 2010, et a fonctionné à cette époque. Voir la réponse d'Alex Miller pour une solution plus moderne.
Quel type de code appelle de Java? Si vous avez une classe générée avec gen-class, appelez-la simplement. Si vous souhaitez appeler une fonction à partir d'un script, regardez l' exemple suivant .
Si vous souhaitez évaluer le code à partir d'une chaîne, à l'intérieur de Java, vous pouvez utiliser le code suivant:
la source
RT.var("namespace", "my-var").deref()
.RT.load("clojure/core");
au début. Quel est le comportement étrange?EDIT: J'ai écrit cette réponse il y a presque trois ans. Dans Clojure 1.6, il existe une API appropriée exactement dans le but d'appeler Clojure depuis Java. Veuillez répondre à Alex Miller pour des informations à jour.
Réponse originale de 2011:
Comme je le vois, le moyen le plus simple (si vous ne générez pas de classe avec la compilation AOT) est d'utiliser clojure.lang.RT pour accéder aux fonctions dans clojure. Avec lui, vous pouvez imiter ce que vous auriez fait dans Clojure (pas besoin de compiler les choses de manière spéciale):
Et en Java:
C'est un peu plus verbeux en Java, mais j'espère qu'il est clair que les morceaux de code sont équivalents.
Cela devrait fonctionner tant que Clojure et les fichiers source (ou fichiers compilés) de votre code Clojure sont sur le chemin des classes.
la source
Je suis d'accord avec la réponse de clartaq, mais j'ai senti que les débutants pourraient également utiliser:
J'ai donc couvert tout cela dans ce billet de blog .
Le code Clojure ressemble à ceci:
La configuration du projet leiningen 1.7.1 ressemble à ceci:
Le code Java ressemble à ceci:
Ou vous pouvez également obtenir tout le code de ce projet sur github .
la source
Cela fonctionne avec Clojure 1.5.0:
la source
Si le cas d'utilisation consiste à inclure un JAR construit avec Clojure dans une application Java, j'ai trouvé qu'il était avantageux d'avoir un espace de noms séparé pour l'interface entre les deux mondes:
L'espace de noms principal peut utiliser l'instance injectée pour accomplir ses tâches:
À des fins de test, l'interface peut être stubbée:
la source
Une autre technique qui fonctionne également avec d'autres langages au-dessus de JVM consiste à déclarer une interface pour les fonctions que vous souhaitez appeler, puis à utiliser la fonction «proxy» pour créer une instance qui les implémente.
la source
Vous pouvez également utiliser la compilation AOT pour créer des fichiers de classe représentant votre code de clojure. Lisez la documentation sur la compilation, la classe gen et les amis dans la documentation de l'API Clojure pour plus de détails sur la façon de procéder, mais vous allez essentiellement créer une classe qui appelle des fonctions clojure pour chaque appel de méthode.
Une autre alternative consiste à utiliser la nouvelle fonctionnalité de defprotocol et de deftype, qui nécessitera également la compilation AOT mais offrira de meilleures performances. Je ne connais pas encore les détails sur la façon de procéder, mais une question sur la liste de diffusion ferait probablement l'affaire.
la source