如何使用循环类型提示编译gen-class类?

时间:2015-05-15 04:48:17

标签: java clojure leiningen clojure-java-interop

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

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

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

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

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

:aot [Foo Bar]

我没有得到循环依赖性错误。相反,我得到ClassNotFoundException:如果我在Foo后首先放置:aot,编译器会在编译Bar时抱怨它不知道Foo,因为^Bar中的-step类型提示。如果我在Bar之后放置:aot,编译器在编译Foo时找不到Bar,因为在Bar.clj中调用了(Foo.)

我目前的解决方案是:

  1. 删除Foo.clj中^Bar定义中的-step类型提示。
  2. 编译这两个类。
  3. 在Foo.clj中的-step中添加类型提示。
  4. 编译Foo(即再次运行'lein compile`)。
  5. 这是有效的,因为第二次编译Foo时,Bar存在,所以编译器不会抱怨。

    有没有办法编译这两个类而不删除并添加类型提示? (或者我应该考虑这种情况的另一种方式?)

1 个答案:

答案 0 :(得分:1)

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

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

然后您可以在以下答案中使用前向声明技术来访问Bar中的new-foo - Forward-declaring a var from another namespace in Clojure?

这当然仍然很糟糕 - 如果有任何其他方法来打破依赖周期,你可能会采取它。如果有意义的话,如何在同一名称空间中定义Foo和Bar?它们看起来非常紧密耦合,尽管有一个抽象的问题描述很难说。