170 votes

L’appel de clojure depuis java

La plupart des hits haut de la page google pour « appel clojure depuis java » est dépassée et vous recommandons d’utiliser clojure.lang.RT pour compiler le code source. Pourriez-vous aider avec une explication claire de comment appeler Clojure depuis java en supposant que vous ont déjà construire un pot du projet clojure et inclus dans le chemin de classe ?

174voto

clartaq Points 2485

Mise à jour: Depuis cette réponse a été publiée, certains des outils disponibles ont changé. Après l'original de la réplique, il y a une mise à jour, y compris des informations sur la façon de construire l'exemple avec les outils actuels.

Il n'est pas aussi simple que l'élaboration d'un pot et d'appeler les méthodes internes. Il semble bien y avoir quelques trucs pour les faire fonctionner. Voici un exemple simple de Clojure fichier qui peut être compilé pour un pot:

(ns com.domain.tiny
  (:gen-class
    :name com.domain.tiny
    :methods [#^{:static true} [binomial [int int] double]]))

(defn binomial
  "Calculate the binomial coefficient."
  [n k]
  (let [a (inc n)]
    (loop [b 1
           c 1]
      (if (> b k)
        c
        (recur (inc b) (* (/ (- a b) b) c))))))

(defn -binomial
  "A Java-callable wrapper around the 'binomial' function."
  [n k]
  (binomial n k))

(defn -main []
  (println (str "(binomial 5 3): " (binomial 5 3)))
  (println (str "(binomial 10042 111): " (binomial 10042 111)))
)

Si vous l'exécutez, vous devriez voir quelque chose comme:

(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...

Et voici un programme Java qui appelle l' -binomial fonction dans l' tiny.jar.

import com.domain.tiny;

public class Main {

    public static void main(String[] args) {
        System.out.println("(binomial 5 3): " + tiny.binomial(5, 3));
        System.out.println("(binomial 10042, 111): " + tiny.binomial(10042, 111));
    }
}

Il est de sortie est:

(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

Le premier morceau de la magie est à l'aide de l' :methods mot-clé dans l' gen-class déclaration. Qui semble être nécessaire pour vous permettre d'accéder à la Clojure la fonction de quelque chose comme des méthodes statiques dans Java.

La deuxième chose est de créer un wrapper fonction qui peut être appelée par Java. Notez que la seconde version de l' -binomial a un tableau de bord en face de lui.

Et bien sûr, le Clojure pot lui-même doit être sur le chemin de classe. Cet exemple utilise la Clojure-1.1.0 jar.

Mise à jour: Cette réponse a été re-testés en utilisant les outils suivants:

  • Clojure 1.5.1
  • Leiningen 2.1.3
  • JDK 1.7.0 mise à Jour du 25

Le Clojure Partie

Tout d'abord créer un projet et associées à la structure de répertoire à l'aide de Leiningen:

C:\projects>lein new com.domain.tiny

Maintenant, changez le répertoire du projet.

C:\projects>cd com.domain.tiny

Dans le répertoire du projet, ouvrez l' project.clj le fichier et modifier le contenu sont comme indiqué ci-dessous.

(defproject com.domain.tiny "0.1.0-SNAPSHOT"
  :description "An example of stand alone Clojure-Java interop"
  :url "http://clarkonium.net/2013/06/java-clojure-interop-an-update/"
  :license {:name "Eclipse Public License"
  :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.5.1"]]
  :aot :all
  :main com.domain.tiny)

Maintenant, assurez-vous que toutes les dépendances (Clojure) sont disponibles.

C:\projects\com.domain.tiny>lein deps

Vous pouvez voir un message sur le téléchargement de la Clojure choque à ce point.

Maintenant éditer le Clojure fichier C:\projects\com.domain.tiny\src\com\domain\tiny.clj contient la Clojure programme indiqué dans la réponse originale à cette question. (Ce fichier a été créé lors de Leiningen créé le projet.)

Une grande partie de la magie ici, c'est dans la déclaration d'espace de noms. L' :gen-class indique au système de créer une classe nommée com.domain.tiny avec une méthode statique unique appelé binomial, une fonction prenant deux arguments entiers et le retour d'un lit double. Il y a deux fonctions de même nommées binomial, un traditionnel Clojure fonction, et -binomial et l'enveloppe accessible à partir de Java. Remarque 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 quelque chose d'autre si vous le souhaitez. L' -main fonction tout à fait quelques appels à la loi binomiale de fonction pour s'assurer que nous obtenons les résultats corrects. Pour ce faire, la compilation de la classe et exécuter le programme.

C:\projects\com.domain.tiny>lein run

Vous devriez voir le résultat indiqué dans la réponse originale à cette question.

Maintenant tout dans un bocal et le mettre dans un endroit commode. Copiez le Clojure jar là aussi.

C:\projects\com.domain.tiny>lein jar
Created C:\projects\com.domain.tiny\target\com.domain.tiny-0.1.0-SNAPSHOT.jar
C:\projects\com.domain.tiny>mkdir \target\lib

C:\projects\com.domain.tiny>copy target\com.domain.tiny-0.1.0-SNAPSHOT.jar target\lib\
        1 file(s) copied.

C:\projects\com.domain.tiny>copy "C:<path to clojure jar>\clojure-1.5.1.jar" target\lib\
        1 file(s) copied.

La Partie Java

Leiningen a une tâche intégrée, lein-javac, qui devrait être en mesure d'aider avec la Java compilation. 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 repository Maven. Les chemins d'accès vers les deux ont des espaces sur mon système. Je suppose que c'est le problème. Java IDE pourrait gérer la compilation et de l'emballage. Mais pour ce post, nous allons de l'ancienne école et de le faire en ligne de commande.

D'abord créer le fichier Main.java avec le contenu indiqué dans la réponse originale à cette question.

Pour compiler java partie

javac -g -cp target\com.domain.tiny-0.1.0-SNAPSHOT.jar -d target\src\com\domain\Main.java

Maintenant, créez un fichier avec des meta-informations à ajouter dans le pot que nous voulons bâtir. En Manifest.txt, ajouter le texte suivant

Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main

Maintenant package tout en un seul gros fichier jar, y compris notre Clojure et le programme de Clojure jar.

C:\projects\com.domain.tiny\target>jar cfm Interop.jar Manifest.txt Main.class lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar

Pour exécuter le programme:

C:\projects\com.domain.tiny\target>java -jar Interop.jar
(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

La sortie est pour l'essentiel identique à celle produite par Clojure seul, mais le résultat a été converti en Java double.

Comme mentionné, un IDE Java sera probablement prendre soin de la malpropre compilation des arguments et de l'emballage.

127voto

Alex Miller Points 28225

Comme de Clojure 1.6.0, il y a un nouveau moyen privilégié pour charger et appeler Clojure fonctions. Cette méthode est préférée à l'appel de la RT directement (et remplace de nombreux autres réponses ici). La javadoc est ici - le principal point d'entrée est - clojure.java.api.Clojure.

Pour rechercher et appeler un Clojure fonction:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);

Fonctions en clojure.core sont automatiquement chargés. D'autres espaces de noms peuvent être chargés via exiger:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));

IFns peuvent être transmises à des fonctions d'ordre supérieur, par exemple, l'exemple ci-dessous passes plus de read:

IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));

La plupart des IFns en Clojure reportez-vous à fonctions. Quelques-uns, cependant, reportez-vous à la non-fonction de valeurs de données. Pour accéder à ces, utiliser deref au lieu de fn:

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);

Parfois (si vous utilisez une autre partie de la Clojure runtime), vous devez vous assurer que le Clojure runtime est correctement initialisé - appel d'une méthode sur le Clojure classe est suffisant pour cela. Si vous n'avez pas besoin d'appeler une méthode sur Clojure, puis il suffit de causer à la classe de charge est suffisante (dans le passé, il y a eu une recommandation similaire à la charge de la RT classe; c'est maintenant préféré):

Class.forName("clojure.java.api.Clojure") 

35voto

Alex Ott Points 14329

Quel type de code appelle Java? Si vous avez une classe générée avec gen-class, appelez-la simplement. Si vous souhaitez appeler la fonction à partir d'un script, consultez l' exemple suivant .

Si vous souhaitez évaluer le code d'une chaîne, à l'intérieur de Java, vous pouvez utiliser le code suivant:

 import clojure.lang.RT;
import clojure.lang.Var;
import clojure.lang.Compiler;
import java.io.StringReader;

public class Foo {
  public static void main(String[] args) throws Exception {
    // Load the Clojure script -- as a side effect this initializes the runtime.
    String str = "(ns user) (defn foo [a b]   (str a \" \" b))";

    //RT.loadResourceScript("foo.clj");
    Compiler.load(new StringReader(str));

    // Get a reference to the foo function.
    Var foo = RT.var("user", "foo");

    // Call it!
    Object result = foo.invoke("Hi", "there");
    System.out.println(result);
  }
}
 

12voto

raek Points 1065

EDIT: j'ai écrit cette réponse presque trois ans. En Clojure 1.6 il y a une bonne API exactement aux fins de l'appel de Clojure à partir de Java. Merci Alex Miller réponse pour des renseignements à jour: http://stackoverflow.com/a/23555959/202121

Réponse originale à cette question à partir de 2011:

Comme je le vois, le moyen le plus simple (si vous n'avez pas de générer une classe avec AOT compilation) est d'utiliser clojure.lang.RT pour accéder à des fonctions dans clojure. Avec elle, vous pouvez imiter ce que vous auriez fait en Clojure (pas besoin de compiler les choses d'une manière particulière):

;; Example usage of the "bar-fn" function from the "foo.ns" namespace from Clojure
(require 'foo.ns)
(foo.ns/bar-fn 1 2 3)

Et en Java:

// Example usage of the "bar-fn" function from the "foo.ns" namespace from Java
import clojure.lang.RT;
import clojure.lang.Symbol;
...
RT.var("clojure.core", "require").invoke(Symbol.intern("foo.ns"));
RT.var("foo.ns", "bar-fn").invoke(1, 2, 3);

C'est un peu plus prolixe en Java, mais j'espère qu'il est clair que les morceaux de code sont équivalentes.

Cela devrait fonctionner aussi longtemps que Clojure et les fichiers source (ou les fichiers compilés) de votre Clojure code est sur le chemin de la classe.

10voto

sandover Points 1110

Je suis d'accord avec clartaq réponse, mais j'ai senti que les débutants pourraient aussi utiliser:

  • étape par étape, des informations sur la façon d'obtenir cette running
  • des informations actuelles de Clojure 1.3 et les versions récentes de leiningen.
  • un Clojure pot, qui comprend également une fonction principale, de sorte qu'il peut fonctionner de manière autonome ou en tant que bibliothèque.

J'ai donc couvert tous que dans ce blog.

Le Clojure code ressemble à ceci:

(ns ThingOne.core
 (:gen-class
    :methods [#^{:static true} [foo [int] void]]))

(defn -foo [i] (println "Hello from Clojure. My input was " i))

(defn -main [] (println "Hello from Clojure -main." ))

Le leiningen 1.7.1 projet d'installation ressemble à ceci:

(defproject ThingOne "1.0.0-SNAPSHOT"
  :description "Hello, Clojure"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :aot [ThingOne.core]
  :main ThingOne.core)

Le code Java ressemble à ceci:

import ThingOne.*;

class HelloJava {
    public static void main(String[] args) {
        System.out.println("Hello from Java!");
        core.foo (12345);
    }
}

Ou vous pouvez également obtenir tout le code de ce projet sur github.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X