从 java 调用 clojure

发布于 2024-08-20 02:44:16 字数 145 浏览 3 评论 0 原文

大多数“从 java 调用 clojure”的热门 google 搜索结果都已过时,建议使用 clojure.lang.RT 来编译源代码。假设您已经从 Clojure 项目构建了一个 jar 并将其包含在类路径中,您能否帮助清楚地解释如何从 Java 调用 Clojure?

Most of the top google hits for "calling clojure from java" are outdated and recommend using clojure.lang.RT to compile the source code. Could you help with a clear explanation of how to call Clojure from Java assuming you have already built a jar from the Clojure project and included it in the classpath?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

与往事干杯 2024-08-27 02:44:16

更新:自从发布此答案以来,一些可用的工具已经发生了变化。在原始答案之后,有一个更新,包括有关如何使用当前工具构建示例的信息。

它并不像编译成 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

第一个魔法是在 gen-class 语句中使用 :methods 关键字。这似乎是让您访问 Clojure 函数所必需的,就像 Java 中的静态方法一样。

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

当然,Clojure jar 本身必须位于类路径上。此示例使用 Clojure-1.1.0 jar。

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

  • Clojure 1.5.1
  • Leiningen 2.1.3
  • JDK 1.7.0 Update 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 告诉系统使用一个名为 binomial 的静态方法创建一个名为 com.domain.tiny 的类,该函数采用两个整数参数并返回一个双精度值。有两个类似命名的函数 binomial(传统的 Clojure 函数)以及可从 Java 访问的 -binomial 和包装器。请注意函数名称 -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 可能会处理混乱的编译参数和打包。

Update: Since this answer was posted, some of the tools available have changed. After the original answer, there is an update including information on how to build the example with current tools.

It isn't quite as simple as compiling to a jar and calling the internal methods. There do seem to be a few tricks to make it all work though. Here's an example of a simple Clojure file that can be compiled to a 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)))
)

If you run it, you should see something like:

(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...

And here's a Java program that calls the -binomial function in the 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));
    }
}

It's output is:

(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

The first piece of magic is using the :methods keyword in the gen-class statement. That seems to be required to let you access the Clojure function something like static methods in Java.

The second thing is to create a wrapper function that can be called by Java. Notice that the second version of -binomial has a dash in front of it.

And of course the Clojure jar itself must be on the class path. This example used the Clojure-1.1.0 jar.

Update: This answer has been re-tested using the following tools:

  • Clojure 1.5.1
  • Leiningen 2.1.3
  • JDK 1.7.0 Update 25

The Clojure Part

First create a project and associated directory structure using Leiningen:

C:\projects>lein new com.domain.tiny

Now, change to the project directory.

C:\projects>cd com.domain.tiny

In the project directory, open the project.clj file and edit it such that the contents are as shown below.

(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)

Now, make sure all of the dependencies (Clojure) are available.

C:\projects\com.domain.tiny>lein deps

You may see a message about downloading the Clojure jar at this point.

Now edit the Clojure file C:\projects\com.domain.tiny\src\com\domain\tiny.clj such that it contains the Clojure program shown in the original answer. (This file was created when Leiningen created the project.)

Much of the magic here is in the namespace declaration. The :gen-class tells the system to create a class named com.domain.tiny with a single static method called binomial, a function taking two integer arguments and returning a double. There are two similarly named functions binomial, a traditional Clojure function, and -binomial and wrapper accessible from Java. Note the hyphen in the function name -binomial. The default prefix is a hyphen, but it can be changed to something else if desired. The -main function just makes a couple of calls to the binomial function to assure that we are getting the correct results. To do that, compile the class and run the program.

C:\projects\com.domain.tiny>lein run

You should see output shown in the original answer.

Now package it up in a jar and put it someplace convenient. Copy the Clojure jar there too.

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.

The Java Part

Leiningen has a built-in task, lein-javac, that should be able to help with the Java compilation. Unfortunately, it seems to be broken in version 2.1.3. It can't find the installed JDK and it can't find the Maven repository. The paths to both have embedded spaces on my system. I assume that is the problem. Any Java IDE could handle the compilation and packaging too. But for this post, we're going old school and doing it at the command line.

First create the file Main.java with the contents shown in the original answer.

To compile java part

javac -g -cp target\com.domain.tiny-0.1.0-SNAPSHOT.jar -d target\src\com\domain\Main.java

Now create a file with some meta-information to add to the jar we want to build. In Manifest.txt, add the following text

Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main

Now package it all up into one big jar file, including our Clojure program and the 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

To run the program:

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

The output is essentially identical to that produced by Clojure alone, but the result has been converted to a Java double.

As mentioned, a Java IDE will probably take care of the messy compilation arguments and the packaging.

记忆消瘦 2024-08-27 02:44:16

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

查找并调用 Clojure 函数:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);

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

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));

IFn 可以传递给更高阶的函数,例如下面的示例将 plus 传递给 read

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

Most < Clojure 中的 code>IFn 指的是函数。然而,有一些涉及非功能数据值。要访问这些,请使用 deref 而不是 fn

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);

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

Class.forName("clojure.java.api.Clojure") 

As of Clojure 1.6.0, there is a new preferred way to load and invoke Clojure functions. This method is now preferred to calling RT directly (and supersedes many of the other answers here). The javadoc is here - the main entry point is clojure.java.api.Clojure.

To lookup and call a Clojure function:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);

Functions in clojure.core are automatically loaded. Other namespaces can be loaded via require:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));

IFns can be passed to higher order functions, e.g. the example below passes plus to read:

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

Most IFns in Clojure refer to functions. A few, however, refer to non-function data values. To access these, use deref instead of fn:

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);

Sometimes (if using some other part of the Clojure runtime), you may need to ensure that the Clojure runtime is properly initialized - calling a method on the Clojure class is sufficient for this purpose. If you do not need to call a method on Clojure, then simply causing the class to load is sufficient (in the past there has been a similar recommendation to load the RT class; this is now preferred):

Class.forName("clojure.java.api.Clojure") 
恏ㄋ傷疤忘ㄋ疼 2024-08-27 02:44:16

编辑这个答案写于2010年,并在当时起作用。请参阅亚历克斯·米勒的答案以获得更现代的解决方案。

从 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);
  }
}

EDIT This answer was written in 2010, and worked at that time. See Alex Miller's answer for more modern solution.

What kind of code are calling from Java? If you have class generated with gen-class, then simply call it. If you want to call function from script, then look to following example.

If you want to evaluate code from string, inside Java, then you can use following code:

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);
  }
}
永言不败 2024-08-27 02:44:16

编辑:我大约三年前写了这个答案。在 Clojure 1.6 中,有一个适当的 API 专门用于从 Java 调用 Clojure。请Alex Miller 的回答了解最新信息。

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)

在 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 代码的源文件(或编译文件)位于类路径上,此操作就应该有效。

EDIT: I wrote this answer almost three years ago. In Clojure 1.6 there is a proper API exactly for the purpose of calling Clojure from Java. Please Alex Miller's answer for up to date information.

Original answer from 2011:

As I see it, the simplest way (if you don't generate a class with AOT compilation) is to use clojure.lang.RT to access functions in clojure. With it you can mimic what you would have done in Clojure (no need to compile things in special ways):

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

And in 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);

It is a bit more verbose in Java, but I hope it's clear that the pieces of code are equivalent.

This should work as long as Clojure and the source files (or compiled files) of your Clojure code is on the classpath.

若沐 2024-08-27 02:44:16

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

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

所以我在 此博文

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." ))

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)

Java 代码如下所示:

import ThingOne.*;

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

或者您也可以从 github 上的这个项目

I agree with clartaq's answer, but I felt that beginners could also use:

  • step-by-step information on how to actually get this running
  • information that's current for Clojure 1.3 and recent versions of leiningen.
  • a Clojure jar that also includes a main function, so it can be run standalone or linked as a library.

So I covered all that in this blog post.

The Clojure code looks like this:

(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." ))

The leiningen 1.7.1 project setup looks like this:

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

The Java code looks like this:

import ThingOne.*;

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

Or you can also get all the code from this project on github.

请恋爱 2024-08-27 02:44:16

这适用于 Clojure 1.5.0:

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)"));
    }
}

This works with Clojure 1.5.0:

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)"));
    }
}
雨落□心尘 2024-08-27 02:44:16

如果用例是在 Java 应用程序中包含使用 Clojure 构建的 JAR,我发现为两个世界之间的接口提供一个单独的命名空间是有益的:

(ns example-app.interop
  (:require [example-app.core :as core])

;; This example covers two-way communication: the Clojure library 
;; relies on the wrapping Java app for some functionality (through
;; an interface that the Clojure library provides and the Java app
;; implements) and the Java app calls the Clojure library to perform 
;; work. The latter case is covered by a class provided by the Clojure lib.
;; 
;; This namespace should be AOT compiled.

;; The interface that the java app can implement
(gen-interface
  :name com.example.WeatherForecast
  :methods [[getTemperature [] Double]])

;; The class that the java app instantiates
(gen-class
  :name com.example.HighTemperatureMailer
  :state state
  :init init
  ;; Dependency injection - take an instance of the previously defined
  ;; interface as a constructor argument
  :constructors {[com.example.WeatherForecast] []}
  :methods [[sendMails [] void]])

(defn -init [weather-forecast]
  [[] {:weather-forecast weather-forecast}])

;; The actual work is done in the core namespace
(defn -sendMails
  [this]
  (core/send-mails (.state this)))

核心命名空间可以使用注入的实例来完成其任务:

(ns example-app.core)

(defn send-mails 
  [{:keys [weather-forecast]}]
  (let [temp (.getTemperature weather-forecast)] ...)) 

用于测试出于目的,接口可以被存根:

(example-app.core/send-mails 
  (reify com.example.WeatherForecast (getTemperature [this] ...)))

If the use case is to include a JAR built with Clojure in a Java application, I have found having a separate namespace for the interface between the two worlds to be beneficial:

(ns example-app.interop
  (:require [example-app.core :as core])

;; This example covers two-way communication: the Clojure library 
;; relies on the wrapping Java app for some functionality (through
;; an interface that the Clojure library provides and the Java app
;; implements) and the Java app calls the Clojure library to perform 
;; work. The latter case is covered by a class provided by the Clojure lib.
;; 
;; This namespace should be AOT compiled.

;; The interface that the java app can implement
(gen-interface
  :name com.example.WeatherForecast
  :methods [[getTemperature [] Double]])

;; The class that the java app instantiates
(gen-class
  :name com.example.HighTemperatureMailer
  :state state
  :init init
  ;; Dependency injection - take an instance of the previously defined
  ;; interface as a constructor argument
  :constructors {[com.example.WeatherForecast] []}
  :methods [[sendMails [] void]])

(defn -init [weather-forecast]
  [[] {:weather-forecast weather-forecast}])

;; The actual work is done in the core namespace
(defn -sendMails
  [this]
  (core/send-mails (.state this)))

The core namespace can use the injected instance to accomplish its tasks:

(ns example-app.core)

(defn send-mails 
  [{:keys [weather-forecast]}]
  (let [temp (.getTemperature weather-forecast)] ...)) 

For testing purposes, the interface can be stubbed:

(example-app.core/send-mails 
  (reify com.example.WeatherForecast (getTemperature [this] ...)))
你另情深 2024-08-27 02:44:16

也适用于 JVM 之上的其他语言的其他技术是为要调用的函数声明一个接口,然后使用“代理”函数创建实现它们的实例。

Other technique that works also with other languages on top of JVM is to declare an interface for functions you want to call and then use 'proxy' function to create instance that implemennts them.

夜唯美灬不弃 2024-08-27 02:44:16

在 Clojure 中,我使用 gen-class,替代方法是从 Java 编写它,如下所示。
我不知道这是否是最好的方法,但很简单。

package mypackage.clojure_interop;

import clojure.java.api.Clojure;
import clojure.lang.*;

public class Interop
{
  //reusable code,add your functions bellow
  //reads a clojure function from a namespace and it returns it as IFn
  public static IFn require = Clojure.var("clojure.core", "require");
  public static IFn getClojureFn(String ns, String fnName)
  {
    require.invoke(Clojure.read(ns));
    IFn fn = Clojure.var(ns, fnName);
    return fn;
  }

  //example using the above for keyword
  public static IFn keywordFn= getClojureFn("clojure.core","keyword");
  public static Object keyword(Object m)
  {
    return keywordFn.invoke(m);
  }

  //any other function will be the same like keyword example
  //IFn first and then a static method with the invoke
  //if many are needed and tedious it can be auto-generated also

}

From Clojure i use gen-class, alternative is to write it from Java like bellow.
I don't know if its the best way, but its simple.

package mypackage.clojure_interop;

import clojure.java.api.Clojure;
import clojure.lang.*;

public class Interop
{
  //reusable code,add your functions bellow
  //reads a clojure function from a namespace and it returns it as IFn
  public static IFn require = Clojure.var("clojure.core", "require");
  public static IFn getClojureFn(String ns, String fnName)
  {
    require.invoke(Clojure.read(ns));
    IFn fn = Clojure.var(ns, fnName);
    return fn;
  }

  //example using the above for keyword
  public static IFn keywordFn= getClojureFn("clojure.core","keyword");
  public static Object keyword(Object m)
  {
    return keywordFn.invoke(m);
  }

  //any other function will be the same like keyword example
  //IFn first and then a static method with the invoke
  //if many are needed and tedious it can be auto-generated also

}
随遇而安 2024-08-27 02:44:16

您还可以使用 AOT 编译来创建代表 clojure 代码的类文件。请阅读 Clojure API 文档中有关编译、gen-class 和朋友的文档,了解有关如何执行此操作的详细信息,但本质上,您将创建一个为每个方法调用调用 clojure 函数的类。

另一种选择是使用新的 defprotocol 和 deftype 功能,这也需要 AOT 编译,但提供更好的性能。我还不知道如何做到这一点的细节,但邮件列表上的问题可能会解决问题。

You can also use AOT compilation to create class files representing your clojure code. Read the documentation about compilation, gen-class and friends in the Clojure API docs for the details about how to do this, but in essence you will create a class that calls clojure functions for each method invocation.

Another alternative is to use the new defprotocol and deftype functionality, which will also require AOT compilation but provide better performance. I don't know the details of how to do this yet, but a question on the mailing list would probably do the trick.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文