【问题标题】:Calling Clojure from Java: Why is the "new" style (clojure.java.api.Clojure) better than the "old" one (gen-class)?从 Java 调用 Clojure:为什么“新”样式 (clojure.java.api.Clojure) 比“旧”样式 (gen-class) 更好?
【发布时间】:2020-09-18 08:12:48
【问题描述】:

读完this great answer to a related question后有些让我困惑:

与 Java 开发人员共享我在 Clojure 中编写的函数有两种可能性

  • 第一个是在 JAR 文件中提供它,以便他们可以调用它,就像我用 Java 编写的一样。听起来很适合 Clojure 宣传。

  • 第二种方法,据称是更好的方法,要求那些 Java 开发人员使用诸如 clojure.lang.IFnclojure.lang.RT 之类的东西,并调用函数,而是将它们的名称作为字符串 (!) 传递只是打电话给他们。

为什么第二种方法“更好”?

【问题讨论】:

  • 我认为你错了,再读一遍那个答案。
  • 使用tiny.binomial(5, 3)调用
  • @m0skit0: 调用tiny.binomials(5, 3) 是“旧方式”——这是我更喜欢的方式。 “新方式”是@alex-miller 在下面用IFn plus = Clojure.var("clojure.core", "+"); 等表达式进一步解释的方式
  • 如果你想从字符串评估代码。你在哪里读到这种方式更好?
  • @m0skit0:也许我必须重新提出我的问题。简而言之:如果我在 Clojure 中编写一个函数,例如(defn foo ...) 我希望我的 Java 朋友能够将它导入并调用它,就好像它是用 Java 编写的一样;即x = foo() - 我确实希望他们做类似RT.invoke("foo") 的事情。我在哪里读到这种方式更好?看看 Alex Miller(顶级 Clojure 专家)怎么说:从 Clojure 1.6.0 开始,有一种新的首选方式来加载和调用 Clojure 函数。此方法现在比直接调用 RT 更受欢迎(并取代此处的许多其他答案)。

标签: clojure clojure-java-interop


【解决方案1】:

你在这里设置了一个错误的二分法。每种方法都涉及创建一个 jar 文件:这就是 JVM 程序的分布方式。但是 Java 代码有 3 种不同的方式来调用 jar 中包含的 Clojure 代码:

  1. 使用 clojure.lang.RT 中的方法来初始化运行时,加载文件,然后查找变量。这是旧的、已弃用的方法。
  2. 使用 clojure.java.api.Clojure 中的方法查找函数并调用它们。这是 (1) 的较新版本,隐藏了一些您可能不小心弄错的杂乱内容。
  3. 使用 Clojure 库中的 gen-class 为 Clojure 函数定义对 Java 更友好的接口。

您仍然可以执行 (3) - 完全没有错误。但是 gen-class 是一个相当笨重的工具,除了最简单的例子,比如暴露一些静态方法,它并不是很有趣,而且使用 Clojure 提供一个“感觉”像 Java API 的 API 也不容易.

但是您知道在提供类似于 Java 的 API 方面有什么好处吗?爪哇!因此,如果您想让 Clojure 库在 Java 中易于使用,我建议您在 Clojure 库中包含一些 Java 代码。由您编写的 Java 代码弥合了语言鸿沟。它通过上述机制 (2) 访问您的 Clojure 代码,并呈现一个 Java 友好的外观,因此外部世界不必知道下面有 Clojure。

amalloy/thrift-gen 是我多年前按照这种方法编写的库的示例。用纯 Clojure 编写它一点也不容易,因为传统的 Java 习惯用法对 Clojure 来说非常陌生,而且它不能很好地支持它们。通过编写我自己的 Java shim,Java 客户端可以获得一个非常舒适的界面来使用,而且我可以编写感觉像 Clojure 的 Clojure,而不是一堆 gen 类的废话。

【讨论】:

  • 我猜你的建议是最好的方法,前提是你想要(或必须)编写 Java 代码。就我而言,我很久以前离开 Java(为了 Python),而 Clojure 的主要观点(除了语言的美)是不必编写 Java。 只是能够公开一些静态方法可能并不代表“完整的 Java 体验”(喜欢类、接口等)——但据我了解,这是对 Clojure 思维方式的一个很好的投射:如果可能的话,只处理普通函数(又名“静态方法”)。
  • 如果您不太关心 Java 消费者,只需像往常一样编写您的 Clojure 代码并让他们通过 (2) 访问它。 gen-class 的东西只是让人头疼,因为它就像编写 Java 一样,只是你必须用这种没人用过的奇怪的迷你语言将它翻译成 Clojure。
  • 为什么 gen-class 方法令人头疼?假设您很乐意提供静态方法:1) 随心所欲地编写 Clojure 函数 2) 编写一个简单的两行包装器 3) 通过 (ns...)“导出”函数 4) 稍微修改 project.clj 5) 构建(uber)jar ...你的 Java 朋友可以称它为“静态方法”,就好像我用 Java PS 写的一样感谢您的宝贵时间!
  • 假设您的库正在定义clojure.core/map(想象它不存在)。你应该给函数什么类型?即使您只想导出像nth 这样无害的东西,它的类型也很糟糕。或者,如果您的函数将地图作为输入?您可以编写大量完全正常的 Clojure 函数,直接暴露给 Java 看起来不太好。另外,您没有 Javadoc!你的 Java 朋友怎么会发现这些东西存在呢?
  • 知道 Java 是静态类型的,我会设计那些 Clojure 函数,使其具有相当“基本”的数据结构作为参数(我认为你可以使用只有字符串或数字作为键的映射 - 毕竟,Clojure 中没有对象)。这是我可以忍受的限制-这是您将拥有的表达能力,例如以 JSON 文件作为参数。我不相信 gen-class 是“笨重”或“头痛”。但是,您的 javadoc 论点可能指向我的问题的答案:推荐的方法显然提供了更好的“Java 体验”。
猜你喜欢
  • 1970-01-01
  • 2014-12-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-10
  • 2019-01-31
  • 2012-04-19
  • 1970-01-01
相关资源
最近更新 更多