1

在 Clojure 中,我使用gen-class的是 Java 库。程序员通常提供两个类,分别实现一个接口和扩展一个类。这两个类应该相互引用,考虑到库的设计方式,很难避免这种循环依赖。

循环不会是一个问题——编译器不必知道它们——除了我试图通过明智地添加类型提示来优化代码(具有巨大的加速)。通过重新组织代码,我已经能够避免编译器对循环依赖的抱怨,并且我已经将问题简化为单一类型提示:

在一个源文件 Foo.clj 中,我有这个函数/方法,这是该类实现的接口所必需的:

(defn -step
  [^Foo this ^Bar bar]
  ...)

另一个源文件 Bar.clj 创建了 Foo 实例的集合,因此我必须在其中引用 Foo 类。在我的 Leiningen project.clj 中,我有这样一行:

:aot [Foo Bar]

我没有收到循环依赖错误。相反,我得到一个ClassNotFoundException: 如果我把Foofirst 放在后面:aot,编译器会抱怨它不知道什么Bar时候编译Foo^Bar因为-step. 如果我Bar先放在后面,编译器在编译时:aot找不到,因为在 Bar.clj 中的调用。FooBar(Foo.)

我目前的解决方案是这样的:

  1. 删除Foo.clj^Bar中定义的类型提示。-step
  2. 编译这两个类。
  3. -step在 Foo.clj 中重新添加类型提示。
  4. 编译Foo(即再次运行“lein compile”)。

这是有效Foo的,因为第二次编译时Bar存在,所以编译器不会抱怨。

有没有办法在不删除和添加类型提示的情况下编译这两个类?(或者我应该以不同的方式思考这种情况?)

4

1 回答 1

1

我的倾向是在 foo 命名空间中为 Foo 添加一个工厂函数。

(defn new-foo [] (Foo.)) ; parameterize to your satisfaction

然后您可以在以下答案中使用前向声明技术从 Bar 访问 new-foo -从 Clojure 中的另一个命名空间前向声明 var?

这当然仍然很棘手 - 如果有任何其他方法可以打破依赖循环,你可能会接受它。如果有意义的话,在同一个命名空间中定义 Foo 和 Bar 怎么样?它们看起来确实非常紧密耦合,尽管很难说抽象的问题描述。

于 2015-05-16T17:23:21.557 回答