Die meisten der Top-Google-Hits für "Clojure aus Java aufrufen" sind veraltet und empfehlen die Verwendung clojure.lang.RT
zum Kompilieren des Quellcodes. Könnten Sie mit einer klaren Erklärung helfen, wie Clojure von Java aus aufgerufen wird, vorausgesetzt, Sie haben bereits ein JAR aus dem Clojure-Projekt erstellt und in den Klassenpfad aufgenommen?
java
clojure
clojure-java-interop
Arthur Ulfeldt
quelle
quelle
Antworten:
Aktualisieren : Seit diese Antwort veröffentlicht wurde, haben sich einige der verfügbaren Tools geändert. Nach der ursprünglichen Antwort gibt es ein Update mit Informationen zum Erstellen des Beispiels mit aktuellen Tools.
Es ist nicht ganz so einfach wie das Kompilieren in ein Glas und das Aufrufen der internen Methoden. Es scheint jedoch ein paar Tricks zu geben, damit alles funktioniert. Hier ist ein Beispiel für eine einfache Clojure-Datei, die zu einem JAR kompiliert werden kann:
Wenn Sie es ausführen, sollten Sie Folgendes sehen:
Und hier ist ein Java-Programm, das die
-binomial
Funktion in der aufrufttiny.jar
.Die Ausgabe lautet:
Das erste Stück Magie ist die Verwendung des
:methods
Schlüsselworts in dergen-class
Anweisung. Dies scheint erforderlich zu sein, damit Sie auf die Clojure-Funktion zugreifen können, ähnlich wie bei statischen Methoden in Java.Die zweite Sache ist, eine Wrapper-Funktion zu erstellen, die von Java aufgerufen werden kann. Beachten Sie, dass die zweite Version von
-binomial
einen Bindestrich vor sich hat.Und natürlich muss sich das Clojure-Glas selbst auf dem Klassenpfad befinden. In diesem Beispiel wurde das Glas Clojure-1.1.0 verwendet.
Update : Diese Antwort wurde mit den folgenden Tools erneut getestet:
Der Clojure-Teil
Erstellen Sie zunächst mit Leiningen ein Projekt und eine zugehörige Verzeichnisstruktur:
Wechseln Sie nun in das Projektverzeichnis.
Öffnen Sie im Projektverzeichnis die
project.clj
Datei und bearbeiten Sie sie so, dass der Inhalt wie unten gezeigt ist.Stellen Sie nun sicher, dass alle Abhängigkeiten (Clojure) verfügbar sind.
Möglicherweise wird an dieser Stelle eine Meldung zum Herunterladen des Clojure-Glases angezeigt.
Bearbeiten Sie nun die Clojure-Datei
C:\projects\com.domain.tiny\src\com\domain\tiny.clj
so, dass sie das in der ursprünglichen Antwort gezeigte Clojure-Programm enthält. (Diese Datei wurde erstellt, als Leiningen das Projekt erstellt hat.)Ein Großteil der Magie liegt hier in der Namespace-Deklaration. Das
:gen-class
weist das System an, eine Klassecom.domain.tiny
mit einer einzelnen statischen Methode namens zu erstellen. Diesebinomial
Funktion verwendet zwei ganzzahlige Argumente und gibt ein Double zurück. Es gibt zwei ähnlich benannte Funktionenbinomial
, eine traditionelle Clojure-Funktion-binomial
und einen Wrapper, auf den über Java zugegriffen werden kann. Beachten Sie den Bindestrich im Funktionsnamen-binomial
. Das Standardpräfix ist ein Bindestrich, kann jedoch bei Bedarf in einen anderen geändert werden. Die-main
Funktion ruft nur ein paar Mal die Binomialfunktion auf, um sicherzustellen, dass wir die richtigen Ergebnisse erhalten. Kompilieren Sie dazu die Klasse und führen Sie das Programm aus.Die Ausgabe sollte in der Originalantwort angezeigt werden.
Packen Sie es jetzt in ein Glas und stellen Sie es an einen geeigneten Ort. Kopieren Sie dort auch das Clojure-Glas.
Der Java-Teil
Leiningen hat eine eingebaute Aufgabe,
lein-javac
die bei der Java-Kompilierung helfen soll. Leider scheint es in Version 2.1.3 kaputt zu sein. Das installierte JDK kann nicht gefunden werden, und das Maven-Repository kann nicht gefunden werden. Die Pfade zu beiden haben eingebettete Leerzeichen in meinem System. Ich nehme an, das ist das Problem. Jede Java-IDE kann auch die Kompilierung und Verpackung übernehmen. Aber für diesen Beitrag gehen wir auf die alte Schule und machen es an der Kommandozeile.Erstellen Sie zuerst die Datei
Main.java
mit dem Inhalt der ursprünglichen Antwort.Java-Teil kompilieren
Erstellen Sie nun eine Datei mit einigen Metainformationen, die Sie dem zu erstellenden JAR hinzufügen möchten. In
Manifest.txt
den folgenden TextPacken Sie jetzt alles in eine große JAR-Datei, einschließlich unseres Clojure-Programms und des Clojure-Jars.
So führen Sie das Programm aus:
Die Ausgabe ist im Wesentlichen identisch mit der von Clojure allein, aber das Ergebnis wurde in ein Java-Double konvertiert.
Wie bereits erwähnt, wird sich eine Java-IDE wahrscheinlich um die chaotischen Kompilierungsargumente und die Verpackung kümmern.
quelle
Ab Clojure 1.6.0 gibt es eine neue bevorzugte Methode zum Laden und Aufrufen von Clojure-Funktionen. Diese Methode wird jetzt dem direkten Aufruf von RT vorgezogen (und ersetzt viele der anderen Antworten hier). Der Javadoc ist hier - der Haupteinstiegspunkt ist
clojure.java.api.Clojure
.So suchen Sie eine Clojure-Funktion und rufen sie auf:
Funktionen in
clojure.core
werden automatisch geladen. Andere Namespaces können über require geladen werden:IFn
s kann zu höherer Ordnung Funktionen übergeben werden, zum Beispiel das folgende Beispiel Pässeplus
zuread
:Die meisten
IFn
s in Clojure beziehen sich auf Funktionen. Einige beziehen sich jedoch auf Nichtfunktionsdatenwerte. Um auf diese zuzugreifen, verwenden Siederef
anstelle vonfn
:Manchmal (wenn Sie einen anderen Teil der Clojure-Laufzeit verwenden) müssen Sie möglicherweise sicherstellen, dass die Clojure-Laufzeit ordnungsgemäß initialisiert ist. Zu diesem Zweck reicht es aus, eine Methode für die Clojure-Klasse aufzurufen. Wenn Sie in Clojure keine Methode aufrufen müssen, reicht es aus, die Klasse einfach laden zu lassen (in der Vergangenheit gab es eine ähnliche Empfehlung zum Laden der RT-Klasse; dies wird jetzt bevorzugt):
quelle
Clojure.read("'(1 2 3")
. Es wäre vernünftig, dies als Erweiterungsanforderung einzureichen, obwohl Sie eine Clojure.quote () bereitstellen oder sie als var verwenden möchten.'(1 2 3)
Sie statt zu schreiben so etwas wieClojure.var("clojure.core", "list").invoke(1,2,3)
. Und die Antwort enthält bereits ein Beispiel für die Verwendungrequire
: Es ist nur eine Variable wie jede andere.BEARBEITEN Diese Antwort wurde 2010 geschrieben und funktionierte zu dieser Zeit. Siehe Alex Millers Antwort für eine modernere Lösung.
Welche Art von Code wird von Java aufgerufen? Wenn Sie eine Klasse mit gen-class generiert haben, rufen Sie sie einfach auf. Wenn Sie die Funktion über ein Skript aufrufen möchten, sehen Sie sich das folgende Beispiel an .
Wenn Sie Code aus einer Zeichenfolge in Java auswerten möchten, können Sie folgenden Code verwenden:
quelle
RT.var("namespace", "my-var").deref()
.RT.load("clojure/core");
am Anfang hinzuzufügen . Was ist das seltsame Verhalten?EDIT: Ich habe diese Antwort vor fast drei Jahren geschrieben. In Clojure 1.6 gibt es eine geeignete API, um Clojure von Java aus aufzurufen. Bitte Alex Millers Antwort für aktuelle Informationen.
Ursprüngliche Antwort von 2011:
Aus meiner Sicht besteht der einfachste Weg (wenn Sie keine Klasse mit AOT-Kompilierung generieren) darin, mit clojure.lang.RT auf Funktionen in clojure zuzugreifen. Damit können Sie nachahmen, was Sie in Clojure getan hätten (Sie müssen die Dinge nicht auf besondere Weise kompilieren):
Und in Java:
In Java ist es etwas ausführlicher, aber ich hoffe, es ist klar, dass die Codeteile gleichwertig sind.
Dies sollte funktionieren, solange sich Clojure und die Quelldateien (oder kompilierten Dateien) Ihres Clojure-Codes im Klassenpfad befinden.
quelle
Ich stimme der Antwort von clartaq zu, aber ich hatte das Gefühl, dass Anfänger auch Folgendes verwenden könnten:
Also habe ich all das in diesem Blog-Beitrag behandelt .
Der Clojure-Code sieht folgendermaßen aus:
Das Projekt-Setup von leiningen 1.7.1 sieht folgendermaßen aus:
Der Java-Code sieht folgendermaßen aus:
Oder Sie können den gesamten Code aus diesem Projekt auf github abrufen .
quelle
Dies funktioniert mit Clojure 1.5.0:
quelle
Wenn der Anwendungsfall darin besteht, eine mit Clojure erstellte JAR in eine Java-Anwendung aufzunehmen, hat es sich als vorteilhaft erwiesen, einen separaten Namespace für die Schnittstelle zwischen den beiden Welten zu haben:
Der Kern-Namespace kann die injizierte Instanz verwenden, um seine Aufgaben auszuführen:
Zu Testzwecken kann die Schnittstelle gestoppt werden:
quelle
Eine andere Technik, die auch mit anderen Sprachen zusätzlich zu JVM funktioniert, besteht darin, eine Schnittstelle für Funktionen zu deklarieren, die Sie aufrufen möchten, und dann die 'Proxy'-Funktion zu verwenden, um eine Instanz zu erstellen, die sie implementiert.
quelle
Sie können auch die AOT-Kompilierung verwenden, um Klassendateien zu erstellen, die Ihren Clojure-Code darstellen. Weitere Informationen hierzu finden Sie in der Dokumentation zu Kompilierung, Gen-Klasse und Freunden in den Clojure-API-Dokumenten. Im Wesentlichen erstellen Sie jedoch eine Klasse, die Clojure-Funktionen für jeden Methodenaufruf aufruft.
Eine andere Alternative ist die Verwendung der neuen Defprotokoll- und Deftype-Funktionalität, die ebenfalls eine AOT-Kompilierung erfordert, aber eine bessere Leistung bietet. Ich weiß noch nicht genau, wie das geht, aber eine Frage auf der Mailingliste würde wahrscheinlich den Trick machen.
quelle