2024 年 Clojure 状态调查!分享您的想法。

欢迎!有关如何使用本站的更多信息,请参见关于页面。

0
Java 互操作

我在学习 Clojure 并目前在对 Swing 和 Java 互操作性进行实验。我拥有如下的 namespace

(ns test.core
  (:gen-class
   :name Testing
   :extends javax.swing.JFrame
   :init init
   :state state
   :constructors {[String] [String]})
  (:import (javax.swing JFrame)))

(defn -init [x]
      [[x] ()])

因此我现在可以用 (let [jf (Testing. "example jframe")].. 来实例化一个 JFrame

我在 :gen-class 中使用的 :init、:state 和 :constructors 标签上有点迷茫。

1) 在这个例子中我需要使用 :state 吗?从我理解来看这
https://docs.clojure.org/clojure.core/gen-class
以及在注释中 "com.example" 的代码中,:state 用于允许对本来不可变的对象进行更改,通过创建一个新的带有更改状态的对象。但在我上面的例子中 JFrame 已经是可变的,因此其实没有必要使用 :state。我的这个想法对吗?

2) :init 是一个返回包含父类构造函数参数和状态的向量的函数。:constructors 的具体用途是什么?我能定义一个自定义的构造函数,使其调用父类构造函数,就像在 Java 中做的那样吗?或者这是不可能的,我应该只初始化对象后调用一个单独的函数,例如

(let [jf (Testing. "example jframe")]
(doto jf
    (.setSize 200 300)
    (.setVisible true)
    etc..

提前感谢

2 个答案

+2

被选中
 
最佳答案

与其选择底层(但可能更具特色)的gen-class路由,您可以使用clojure.core/proxy获得良好的效果。使用代理允许您定义一个存根类实现,可以子类化现有类,调用超类方法实现,覆盖等,而不必走genclass要求的AOT编译路由。缺点是它不这么快(例如,如果您正在代理一个对象,其中的方法调用是热点路径,那么可能不是很好,而且需要使用gen-class或其他方法)。事实上,这在包装Swing组件中非常常见(甚至seesaw就是这样做的)。

我的建议是尝试(如果基于接口)reify | deftype,然后proxy,然后gen-class,然后如果您觉得更容易或者愿意,则使用Java。我的大多数用例都通过基本clojure互操作解决了,尽管不需要gen-class。

但是在上述情况下,jframe已经可变,所以根本不需要使用:state。这种思考方式正确吗?

如果您可以获取JFrame的句柄,那么通过互操作您就可以对其进行修改。因为这个类继承自它,所以这是合理的。“状态”实现是一种clojure特定的实施细节,用于提供一种提供自己的东西以及生成类的方式(而不是说一组单独的实例变量,您通常只是一个包含原子以及您想要进行变异的引用状态的映射)。

或者根本不可能,您就应该在对象初始化后调用一个单独的函数,类似于

您可以定义自己的构造函数并将它们映射到超类构造函数,如这篇有用的指南中所示。我认为“初始化后”是一种非常好的方法,它可以限制必须放入gen-class中的类特定杂项。由于您仍然可以通过互操作操作对象,所以这是一个可行且常见的选项。

非常感谢两位抽出时间回答。我在我的小型测试项目中使用seesaw,但一些组件缺失(JDesktopPane,JInternalFrame),因此我不得不研究gen-class并感到困惑(还阅读了kotka.de指南,在google的gen-class搜索结果中位居前三位:)
汤姆,现在更清晰了,我将研究reify/deftype。
0

您可以使用:constructors来定义自己的构造函数,但没有比使用Seesaw更好的解决方案,Seesaw是Swing的出色Clojure包装器。

https://github.com/daveray/seesaw

...