从java调用clojure

Art*_*ldt 161 java clojure clojure-java-interop

"从java调用clojure"的大多数顶级谷歌点击都已过时,建议使用clojure.lang.RT编译源代码.如果您已经从Clojure项目中构建了一个jar并将其包含在类路径中,您能否帮助清楚地解释如何从Java调用Clojure?

cla*_*taq 158

更新:由于此答案已发布,因此可用的一些工具已更改.在原始答案之后,有一个更新,包括有关如何使用当前工具构建示例的信息.

它不像编译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)))
)
Run Code Online (Sandbox Code Playgroud)

如果你运行它,你应该看到类似的东西:

(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...
Run Code Online (Sandbox Code Playgroud)

这是一个调用-binomial函数的Java程序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));
    }
}
Run Code Online (Sandbox Code Playgroud)

它的输出是:

(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263
Run Code Online (Sandbox Code Playgroud)

第一部分魔法是:methodsgen-class声明中使用关键字.这似乎需要让您访问Clojure函数,就像Java中的静态方法一样.

第二件事是创建一个可以由Java调用的包装函数.请注意,第二个版本-binomial在它前面有一个破折号.

当然,Clojure jar本身必须在类路径上.这个例子使用了Clojure-1.1.0 jar.

更新:此答案已使用以下工具重新测试:

  • Clojure 1.5.1
  • 莱宁根2.1.3
  • JDK 1.7.0更新25

Clojure部分

首先使用Leiningen创建一个项目和相关的目录结构:

C:\projects>lein new com.domain.tiny
Run Code Online (Sandbox Code Playgroud)

现在,切换到项目目录.

C:\projects>cd com.domain.tiny
Run Code Online (Sandbox Code Playgroud)

在项目目录中,打开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)
Run Code Online (Sandbox Code Playgroud)

现在,确保所有依赖项(Clojure)都可用.

C:\projects\com.domain.tiny>lein deps
Run Code Online (Sandbox Code Playgroud)

此时您可能会看到有关下载Clojure jar的消息.

现在编辑Clojure文件C:\projects\com.domain.tiny\src\com\domain\tiny.clj,使其包含原始答案中显示的Clojure程序.(此文件是在Leiningen创建项目时创建的.)

这里的大部分魔力都在命名空间声明中.该:gen-class告诉系统创建一个名为类com.domain.tiny有称为单一的静态方法binomial,一个函数接受两个整数参数并返回一个double.有两个类似命名的函数binomial,一个传统的Clojure函数,以及-binomial可从Java访问的包装器.请注意函数名称中的连字符-binomial.默认前缀是连字符,但如果需要,可以将其更改为其他内容.该-main函数只是对二项式函数进行了几次调用,以确保我们得到正确的结果.为此,编译该类并运行该程序.

C:\projects\com.domain.tiny>lein run
Run Code Online (Sandbox Code Playgroud)

您应该看到原始答案中显示的输出.

现在把它打包放在一个罐子里,放在方便的地方.在那里复制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.
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

现在创建一个包含一些元信息的文件,以添加到我们要构建的jar中.在Manifest.txt,添加以下文本

Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main
Run Code Online (Sandbox Code Playgroud)

现在将它们打包成一个大的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
Run Code Online (Sandbox Code Playgroud)

要运行该程序:

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

输出与Clojure单独生成的输出基本相同,但结果已转换为Java double.

如前所述,Java IDE可能会处理混乱的编译参数和包装.

  • @Belun,确定你可以用它作为一个例子 - 我很受宠若惊."#^ {:static true}"将一些元数据附加到函数,指示二项式是静态函数.在这种情况下需要它,因为在Java端,我们从main调用函数 - 一个静态函数.如果二项式不是静态的,那么在Java端编译main函数会产生关于"非静态方法二项式(int,int)不能从静态上下文引用"的错误消息.Object Mentor站点上还有其他示例. (4认同)
  • 这里没有提到一件至关重要的事情 - 将Clojure文件编译为Java类,你需要:(编译'com.domain.tiny) (4认同)
  • 1.我可以把你的例子放在http://clojuredocs.org作为"ns"宏的例子吗?2.什么是前面的#^:方法[#^ {:静态真实} [二项式[int int] double]]"(我是新手)? (3认同)

Ale*_*ler 118

从Clojure 1.6.0开始,有一种新的首选方法来加载和调用Clojure函数.此方法现在更倾向于直接调用RT(并在此处取代许多其他答案).javadoc就在这里 - 主要入口点是clojure.java.api.Clojure.

要查找和调用Clojure函数:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);
Run Code Online (Sandbox Code Playgroud)

函数in clojure.core会自动加载.其他命名空间可以通过require加载:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));
Run Code Online (Sandbox Code Playgroud)

IFns可以传递给更高阶的函数,例如下面的例子传递plusread:

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

IFnClojure中的大多数s都是指函数.但是,有一些是指非功能数据值.要访问这些,请使用deref而不是fn:

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);
Run Code Online (Sandbox Code Playgroud)

有时(如果使用Clojure运行时的其他部分),您可能需要确保正确初始化Clojure运行时 - 在Clojure类上调用方法就足够了.如果你不需要在Clojure上调用一个方法,那么只需要加载类就足够了(过去有类似的建议来加载RT类;现在这是首选):

Class.forName("clojure.java.api.Clojure") 
Run Code Online (Sandbox Code Playgroud)

  • 我认为只有一些特殊形式不存在作为变量.一种解决方法是通过`Clojure.read("'(1 2 3")`访问它.虽然提供Clojure.quote()或使其作为var工作,但将其作为增强请求提交是合理的. . (2认同)

Ale*_*Ott 34

编辑这个答案写于2010年,并在那时工作.请参阅Alex Miller对更现代化解决方案的回答.

从Java调用什么样的代码?如果你有使用gen-class生成的类,那么只需调用它.如果要从脚本调用函数,请查看以下示例.

如果要在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);
  }
}
Run Code Online (Sandbox Code Playgroud)


rae*_*aek 12

编辑:差不多三年前我写了这个答案.在Clojure 1.6中,有一个恰当的API,用于从Java调用Clojure.请Alex Miller回答最新信息:https://stackoverflow.com/a/23555959/202121

2011年的原始答案:

在我看来,最简单的方法(如果你不产生与AOT编译的类)是使用clojure.lang.RT访问Clojure的功能.有了它,你可以模仿你在Clojure中所做的事情(不需要以特殊方式编译东西):

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

在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);
Run Code Online (Sandbox Code Playgroud)

它在Java中有点冗长,但我希望很明显代码片段是等价的.

只要Clojure和Clojure代码的源文件(或编译文件)在类路径上,这就应该可以工作.


san*_*ver 10

我同意clartaq的回答,但我觉得初学者也可以使用:

  • 有关如何实际运行的逐步信息
  • Clojure 1.3和leiningen最新版本的最新信息.
  • 一个Clojure jar,它还包含一个main函数,因此它可以独立运行作为库链接.

所以我在这篇博客文章中介绍了所有内容.

Clojure代码如下所示:

(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." ))
Run Code Online (Sandbox Code Playgroud)

leiningen 1.7.1项目设置如下所示:

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

Java代码如下所示:

import ThingOne.*;

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

或者你也可以在github上获得这个项目的所有代码.