Обновление : с момента публикации этого ответа некоторые доступные инструменты изменились. После первоначального ответа есть обновление, включающее информацию о том, как построить пример с использованием текущих инструментов.
Это не так просто, как компилирование в jar и вызов внутренних методов. Кажется, есть несколько хитростей, чтобы заставить все это работать. Вот пример простого файла Clojure, который можно скомпилировать в jar:
(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)))
)
Если вы запустите его, вы должны увидеть что-то вроде:
(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...
А вот Java-программа, которая вызывает функцию -binomial
в 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));
}
}
Вывод:
(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263
Первая часть магии использует ключевое слово :methods
в выражении gen-class
. Похоже, что это необходимо для того, чтобы вы могли получить доступ к функции Clojure, например, статическим методам в Java.
Второе - создать функцию-обертку, которая может быть вызвана Java. Обратите внимание, что перед второй версией -binomial
стоит тире.
И, конечно, сама банка Clojure должна быть на пути класса. В этом примере использовался jar Clojure-1.1.0.
Обновление : Этот ответ был повторно протестирован с использованием следующих инструментов:
- Clojure 1.5.1
- Leiningen 2.1.3
- JDK 1.7.0 Обновление 25
Закрытая часть
Сначала создайте проект и связанную структуру каталогов, используя Leiningen:
C:\projects>lein new com.domain.tiny
Теперь перейдите в каталог проекта.
C:\projects>cd com.domain.tiny
В каталоге проекта откройте файл project.clj
и отредактируйте его так, чтобы его содержимое было таким, как показано ниже.
(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)
Теперь убедитесь, что все зависимости (Clojure) доступны.
C:\projects\com.domain.tiny>lein deps
В этот момент вы можете увидеть сообщение о загрузке банки Clojure.
Теперь отредактируйте файл Clojure C:\projects\com.domain.tiny\src\com\domain\tiny.clj
так, чтобы он содержал программу Clojure, показанную в исходном ответе. (Этот файл был создан, когда Лейнинген создал проект.)
Большая часть волшебства здесь заключается в объявлении пространства имен. :gen-class
говорит системе создать класс с именем com.domain.tiny
с помощью одного статического метода с именем binomial
, который принимает два целочисленных аргумента и возвращает двойной тип. Есть две функции с одинаковыми именами binomial
, традиционная функция Clojure и -binomial
и оболочка, доступная из Java. Обратите внимание на дефис в имени функции -binomial
. Префикс по умолчанию - дефис, но при желании его можно изменить на что-то другое. Функция -main
просто выполняет пару вызовов биномиальной функции, чтобы убедиться, что мы получаем правильные результаты. Для этого скомпилируйте класс и запустите программу.
C:\projects\com.domain.tiny>lein run
Вы должны увидеть результат, показанный в исходном ответе.
Теперь упакуйте его в банку и положите в удобное место. Скопируйте туда банку Clojure.
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.
Часть Java
В Leiningen есть встроенная задача lein-javac
, которая должна помочь при компиляции Java. К сожалению, в версии 2.1.3 он не работает. Он не может найти установленный JDK и не может найти репозиторий Maven. Пути к обоим имеют встроенные пробелы в моей системе. Я предполагаю, что это проблема. Любая Java IDE также может обрабатывать компиляцию и упаковку. Но для этого поста мы идем в старую школу и делаем это в командной строке.
Сначала создайте файл Main.java
с содержимым, показанным в исходном ответе.
Для компиляции Java-части
javac -g -cp target\com.domain.tiny-0.1.0-SNAPSHOT.jar -d target\src\com\domain\Main.java
Теперь создайте файл с некоторой мета-информацией для добавления в банку, которую мы хотим построить. В Manifest.txt
добавьте следующий текст
Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main
Теперь упакуйте все это в один большой файл jar, включая нашу программу Clojure и jar Clojure.
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
Для запуска программы:
C:\projects\com.domain.tiny\target>java -jar Interop.jar
(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263
Вывод практически идентичен выводу, произведенному только Clojure, но результат был преобразован в Java double.
Как уже упоминалось, Java IDE, вероятно, позаботится о беспорядочных аргументах компиляции и упаковке.