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

欢迎!请参阅关于页面以了解如何进行更多信息。

0 投票
Java互操作

我正在学习Clojure,目前正在尝试Swing与Java互操作性。我有这个命名空间

(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编译路由。缺点是它不如速度快(例如,如果您正在代理一个方法调用位于热路径上的对象,那么这可能不是很好,而genclass或其他方法则是必要的)。事实上,这很常见于包装Swing组件(包括seesaw也这样做)。

我的建议是尝试(如果是基于接口的)reify | deftype,然后是proxy,然后是gen-class,然后如果是更简单或者您愿意,就使用java。我的大多数用例都是通过基本的clojure互操作处理的,尽管不需要gen-class。

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

如果您能控制JFrame,那么通过互操作确实可以修改它。由于这个类继承了它,所以这是可能的。"状态"实现是clojure特定的一种实现细节,用于以惯用的方式包装自己的东西以及生成的类(而不是,比如,一大堆单独的实例变量,您通常只需包装一个状态映射 - 该映射可以包括原子以及您需要的任何东西)。

或者,这根本不可能,应该在对象初始化后调用一个单独的函数,例如

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

by
非常感谢两位抽出时间来回答。我正在使用seesaw在我的小测试项目中,但一些组件缺失(JDesktopPane,JInternalFrame),因此我不得不深入研究gen-class,并有点困惑(也读了一遍kotka.de指南,当您在Google上搜索gen-class时,这是前三链接之一)。
汤姆,现在已经更清晰了,接下来会查找reify/deftypes。
0 投票
by

您可以使用:constructors来创建自己的构造函数,但可能一个更好的解决方案是使用Seesaw,这是一个针对Swing的优秀的Clojure包装器。

https://github.com/daveray/seesaw

...