【问题标题】:How to pass a typed collection from clojure to java?如何将类型化集合从 clojure 传递给 java?
【发布时间】:2010-09-10 22:12:13
【问题描述】:

我知道 clojure/java 互操作的基础知识:从 clojure 调用 java,反之亦然。但是,我无法将类型化集合从 clojure 返回到 java。我试图从调用clojure的java代码中看到List<TypedObject>这种性质的东西。

Java Object:

public class TypedObject {
    private OtherType1 _prop1;
    public OtherType1 getProp1() {
        return _prop1;
    }
    public void setProp1(OtherType1 prop1) {
        _prop1 = prop1;
    }
}

CLojure method:

(defn -createListOfTypedObjects
      "Creates and returns a list of TypedObjects"
      [input]
      ;Do work here  to create and return list of TypedObjects
      [typedObj1, typedObj2, typedObj3])

(:gen-class
 :name some.namespace
 :methods [createListofTypedObjects[String] ????])

假设我正在使用 clojure 编写一个 API,该 API 将作为 jar 文件分发,以便从 java 中使用。我的问题真的是如何通过什么来代替 ???? AOT 的 :gen-class 上面的问号,以便程序员使用我的 api 在 java 中编写一段代码,例如可以在 eclipse 中获得适当的智能感知/代码完成(即:createListofTypedObjects() returns List<TypedObject>)。

【问题讨论】:

  • 一个调用 clojure 的 java 代码的简短示例真的可以帮助我理解这个:)
  • 感谢 Alex 和 Stuart 的回答。它们很有意义,但不是我想要的。希望我的问题现在不那么模棱两可了。

标签: java interop clojure aot


【解决方案1】:

其他人说得对,Clojure 不保证返回集合中的元素类型等。(实际上,JVM 也不保证集合中元素的类型——这完全由 javac 处理。)

但是,我可以看到向其他 Java 程序员提供 API 的价值,该 API 指定一个接口,该接口声明以各种方式参数化的返回值(或参数);如果您希望在现有的 Java 环境中使用 Clojure 而不会引起轰动,这将特别有吸引力。

目前这需要两步流程:

  • 定义一个单独的接口(在 Java 中!),根据需要指定参数化类型
  • 定义您的 gen-class 命名空间(或 proxyreify 实例),以便它实现该接口

(Clojure 确实提供了一个 definterface 表单,可以让您避免单独的 Java 接口定义,但是 definterface 就像 Clojure 的其余部分一样,不提供指定参数化类型。也许有一天......: -))

例如

public interface IFoo {
    List<TypedObject> createListOfTypedObjects ();
}

然后是你的 gen-class 命名空间:

(ns your.ns.FooImpl
  (:gen-class
    :implements [IFoo]))
(defn -createListOfTypedObjects
  []
  [typedObj1, typedObj2, typedObj3])

当您的用户创建 FooImpl 的实例时,他们将例如获取代码完成指示该方法返回 List&lt;TypedObject&gt; 而不是 Object 或未参数化的 List 类型。

如果您使用的是健全的构建工具(例如maven、gradle 或正确配置的 ant),那么您可以将 Java 接口放入您的 Clojure 项目中,这样跨语言的依赖就会得到处理。

【讨论】:

  • V 有用的答案。为在 java env 中运行提供适当的强类型定义至关重要。毕竟类型很少不出现,因为大多数工作都涉及集合,通常我们想指定它们包含的内容。
【解决方案2】:

如果您尝试将 List&lt;String&gt; 之类的内容传递给 java 方法,则无需担心。类型参数(例如,String)仅用于 javac 编译器,因此任何 List 在运行时都可以正常工作。

另一方面,如果您尝试传递特定对象类型的数组(例如,String[]),则可以使用各种 -array 函数:

user=> (make-array String 10)            ; an empty String array
#<String[] [Ljava.lang.String;@78878c4c>
user=> (into-array ["foo" "bar"])        ; array type inferred from first element
#<String[] [Ljava.lang.String;@743fbbfc>
user=> (into-array Number [1.2 5 7N])    ; explicit type array
#<Number[] [Ljava.lang.Number;@7433b121>

【讨论】:

    【解决方案3】:

    您无需担心 Clojure 中的泛型(类型化集合)。泛型实际上只是 Java 编译器的类型提示。在一个正在运行的 Java 程序中,List&lt;String&gt;List&lt;Object&gt; 实际上是相同的。

    因此,例如,包含字符串的 Clojure 向量已经是 List&lt;String&gt;,无需转换。

    【讨论】:

    • 不正确。我会更准确地说,您通常不需要担心 Clojure 中的类型化集合。由于这个问题专门处理 Java 互操作,因此重要的是要指出给定集合可能在某些时候被传递回 Java 方法。在这种情况下,如果方法名称被重载并采用不同元素类型的集合,则集合中元素的类型可能很重要。我知道这可能是晦涩难懂的……但它确实存在。
    • 我不知道如何在 Clojure 中表示精确的泛型实例化。例如,可以写java.lang.ArrayList,但不能写java.langArrayList&lt;String&gt;。如果你是对的,@Jason,这种缺失会是 Clojure-Java 互操作中的一个漏洞吗?由于协变集合类型不可表示,Java 中是否存在无法从 Clojure 调用的 API?上面和下面的答案表明不是,也就是说,我们还好,至少现在是这样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-17
    • 1970-01-01
    • 1970-01-01
    • 2020-09-11
    • 2021-04-02
    • 2014-11-17
    相关资源
    最近更新 更多