从java调用clojure
谷歌最热门的“从java调用clojure”已经过时,建议使用从java调用clojure,java,clojure,clojure-java-interop,Java,Clojure,Clojure Java Interop,谷歌最热门的“从java调用clojure”已经过时,建议使用clojure.lang.RT编译源代码。假设您已经从Clojure项目中构建了一个jar并将其包含在类路径中,您能帮助清楚地解释如何从Java调用Clojure吗?EDIT这个答案是在2010年编写的,当时还有效。有关更现代的解决方案,请参见Alex Miller的答案 从Java调用什么样的代码?如果您使用gen类生成了类,那么只需调用它。若要从脚本调用函数,请查看 如果要从Java内部的字符串计算代码,则可以使用以下代码: im
clojure.lang.RT
编译源代码。假设您已经从Clojure项目中构建了一个jar并将其包含在类路径中,您能帮助清楚地解释如何从Java调用Clojure吗?EDIT这个答案是在2010年编写的,当时还有效。有关更现代的解决方案,请参见Alex Miller的答案
从Java调用什么样的代码?如果您使用gen类生成了类,那么只需调用它。若要从脚本调用函数,请查看
如果要从Java内部的字符串计算代码,则可以使用以下代码:
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);
}
}
您还可以使用AOT编译来创建表示clojure代码的类文件。阅读Clojure API文档中有关编译、gen类和friends的文档,了解如何执行此操作的详细信息,但本质上,您将创建一个类,为每个方法调用调用Clojure函数
另一种选择是使用新的defprotocol和deftype功能,这也需要AOT编译,但可以提供更好的性能。我还不知道如何做到这一点的细节,但邮件列表上的一个问题可能会起到作用。更新:由于这个答案已经发布,一些可用的工具已经更改。在最初的答案之后,会有一个更新,包括如何使用当前工具构建示例的信息 它不像编译到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程序,它调用tiny.jar
中的-binomial
函数
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
第一个神奇之处是在gen class
语句中使用:methods
关键字。这似乎是访问Clojure函数所必需的,类似于Java中的静态方法
第二件事是创建一个可以被Java调用的包装器函数。请注意,-binomial
的第二个版本前面有一个破折号
当然Clojure jar本身必须在类路径上。本例使用了Clojure-1.1.0 jar
更新:此答案已使用以下工具重新测试:
- Clojure 1.5.1
- 莱宁根2.1.3
- JDK 1.7.0更新25
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类
告诉系统创建一个名为com.domain.tiny
的类,该类使用一个名为二项式
的静态方法,该函数使用两个整数参数并返回一个双精度参数。有两个名称类似的函数二项式
,一个是传统的Clojure函数,还有-二项式
和可从Java访问的包装器。注意函数名-二项式中的连字符。默认前缀是连字符,但如果需要,可以将其更改为其他前缀。-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.
现在创建一个包含一些元信息的文件,添加到我们想要构建的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双精度
如前所述,Java IDE可能会处理混乱的编译参数和打包问题。在JVM上与其他语言一起工作的另一种技术是为要调用的函数声明一个接口,然后使用“代理”函数创建实现它们的实例。编辑:我几乎写了这个答案三年前。在Clojure 1.6中,有一个合适的API正好用于从Java调用Clojure。请提供最新信息
2011年的原始答案:
在我看来,最简单的方法(如果不使用AOT编译生成类)是使用clojure.lang.RT访问clojure中的函数。使用它,您可以模仿Clojure中的操作(无需以特殊方式编译):
在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);
在Java中有点冗长,但我希望清楚的是,代码片段是等效的
只要Clojure和Clojure代码的源文件(或编译文件)在类路径上,这就应该可以工作。我同意clartaq的答案,但我觉得初学者也可以使用:
- 关于如何实际运行此功能的逐步信息
- Clojure 1.3和leiningen最新版本的最新信息李>
- 一个Clojure罐子,也包括一个mai
;; Example usage of the "bar-fn" function from the "foo.ns" namespace from Clojure
(require 'foo.ns)
(foo.ns/bar-fn 1 2 3)
// 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);
(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." ))
(defproject ThingOne "1.0.0-SNAPSHOT"
:description "Hello, Clojure"
:dependencies [[org.clojure/clojure "1.3.0"]]
:aot [ThingOne.core]
:main ThingOne.core)
import ThingOne.*;
class HelloJava {
public static void main(String[] args) {
System.out.println("Hello from Java!");
core.foo (12345);
}
}
public class CljTest {
public static Object evalClj(String a) {
return clojure.lang.Compiler.load(new java.io.StringReader(a));
}
public static void main(String[] args) {
new clojure.lang.RT(); // needed since 1.5.0
System.out.println(evalClj("(+ 1 2)"));
}
}
IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));
IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));
IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);
Class.forName("clojure.java.api.Clojure")