Update:自从发布这个答案以来,一些可用的工具已经发生了变化。在原始答案之后,有一个更新,包括有关如何使用当前工具构建示例的信息。
它并不像编译成 jar 并调用内部方法那么简单。不过,似乎确实有一些技巧可以让这一切发挥作用。下面是一个可以编译为 jar 的简单 Clojure 文件的示例:
(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 jar。
Update:这个答案已经使用以下工具重新测试:
- Clojure 1.5.1
- 莱宁根2.1.3
- JDK 1.7.0 更新 25
Clojure 部分
首先使用 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 jar 的消息。
现在编辑 Clojure 文件C:\projects\com.domain.tiny\src\com\domain\tiny.clj
这样它就包含了原始答案中显示的 Clojure 程序。 (此文件是在 Leiningen 创建项目时创建的。)
这里的大部分魔力都在于命名空间声明。这:gen-class
告诉系统创建一个名为com.domain.tiny
使用一个名为的静态方法binomial
,一个接受两个整数参数并返回一个双精度值的函数。有两个类似名称的函数binomial
,一个传统的 Clojure 函数,以及-binomial
和可从 Java 访问的包装器。注意函数名称中的连字符-binomial
。默认前缀是连字符,但如果需要,可以将其更改为其他内容。这-main
函数只是对二项式函数进行几次调用,以确保我们得到正确的结果。为此,请编译该类并运行该程序。
C:\projects\com.domain.tiny>lein run
您应该看到原始答案中显示的输出。
现在将其包装在罐子中并放在方便的地方。将 Clojure jar 也复制到那里。
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
现在创建一个包含一些元信息的文件以添加到我们想要构建的 jar 中。在Manifest.txt
,添加以下文本
Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main
现在将其全部打包到一个大 jar 文件中,包括我们的 Clojure 程序和 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
运行程序:
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 可能会处理混乱的编译参数和打包。