如何使用Clojure代理创建类似java的类?

时间:2013-03-17 10:45:42

标签: clojure

我想在Clojure中创建一个具有属性和方法的对象,我读到gen-class和proxy可以完成我需要的工作但是它的实现对我来说非常混乱。

我想使用代理来避免AOT编译步骤,我读到它并且我虽然我更好地学习如何使用更容易的两个

这是我想在Clojure中做的事情

Java代码:

public class MyClass {
    public float myFloat;

    MyClass( float _myFloat ) {
        myFloat = _myFloat
    }

    public void showNumber() {
        println( myFloat );
    }
}

我正在努力使用代理将代码转换为Clojure,任何帮助都将非常感激


更新

显然 deftype 更适合我的目的,但我仍然在努力实施

这是我的Clojure代码:

(deftype Particle [x y]
  Object
  (render [this]
    (no-stroke)
    (fill 200 30 180)
    (ellipse x y 200 200)))

我需要指定一个协议,我不确定要使用哪个协议,所以我正在使用Object,因为我正在尝试创建类似于java的对象但是我得到了以下错误消息:< / p>

无法在接口中定义方法:render

我正在使用quill,这是Clojure的处理端口,如果这有帮助


更新2:

好吧我设法得到一个有效的defprotocol和deftype组合,但还有一件事我需要学习如何做,那就是向我的班级添加成员变量或属性,这是我的clojure代码:

(defprotocol ParticleProtocol
  (update [this])
  (render [this]))

(deftype Particle [position]
  ParticleProtocol
  (update [this])
  (render [this]
    (no-stroke)
    (fill 200 30 180)
    (ellipse (.x position) (.y position) 20 20)))

对于这个对象,我想添加一些像radius这样的变量,任何想法?

2 个答案:

答案 0 :(得分:3)

我同意在{Clojure中deftype(或可能defrecord)比proxy更好,但最后请看我的评论以考虑所有可能性。

对于UPDATE 2之后的问题。

您可以通过在arglist中指定它们来为记录添加“属性”:

(deftype Particle [position radius prop3 prop4]
   ...
 )

请记住,Clojure中的类型是不可变的,因此在创建实体后没有设置属性的概念。如果某些属性是可选的,建议最佳做法是创建辅助“工厂”方法,例如:

(defn make-particle 
  ([position] (Particle. position nil nil nil))
  ([position radius] (Particle. position radius nil nil))
  ;; etc. add more here as needed
  )

要考虑的一个选项是完全删除类型,只使用地图,其中包含您需要的“属性/字段”。当您需要实现抽象时,类型很有用。对于您的ParticleProtocol - 它提供的价值是多少?协议旨在提供一种具有多态性的方法,那么您将拥有此协议的多个实现吗?

Chas Emerick深入介绍了如何在Clojure中选择可能对您有帮助的数据类型:http://cemerick.com/2011/07/05/flowchart-for-choosing-the-right-clojure-type-definition-form/


[更新显示示例地图实施]

使用“属性”构建地图并检索该属性:

(def mymap {:myfloat 3.1415926})
(println "myfloat has value:" (:myfloat mymap))

要提供其他功能,例如“渲染”功能,只需创建一个接受带有所需键的地图的fn:

;; the details are bogus, just showing the syntax
(defn render [m]
  (no-stroke)
  (fill (:radius m) (:position m))
  (do-something-else (:position m)))      

对于update,如果您要更新粒子贴图中的值,则需要创建新贴图,而不是更新现有贴图。

(def myparticle {:position 100 :radius 25})

(defn change-pos [particle-map new-pos]
  (assoc-in particle-map [:position] new-pos))

(let [new-particle (change-pos myparticle 300)]
  (println new-particle))
;; prints out {:position 300 :radius 25}
;; orig myparticle still has value {:position 100 :radius 25}

;; or do it directly
(println (assoc myparticle :position 300))
;; prints out {:position 300 :radius 25}

答案 1 :(得分:1)

您可以在position旁边添加“变量”,如下所示:

(deftype Particle [position radius]
   ...
  )

positionradius不是真正的变量,它们更像是final属性。如果你需要“改变”它们,你应该将原子存储在它们中,如下所示:

(Particle. (atom (Position. 3 4)) (atom 5.0))

但是你应该注意@ m0skit0的建议,不再考虑对象和类,并开始考虑函数和不可变数据结构。