【发布时间】:2010-12-07 17:31:23
【问题描述】:
我正在学习如何在 Clojure 中扩展 Java 类,但我没有看到声明新成员变量的方法;我只看到方法的方法。
(ns test.aclass
(:gen-class
:methods [[foo [] String]]))
是否有:members 关键字或其他声明成员变量的方式?
【问题讨论】:
标签: clojure
我正在学习如何在 Clojure 中扩展 Java 类,但我没有看到声明新成员变量的方法;我只看到方法的方法。
(ns test.aclass
(:gen-class
:methods [[foo [] String]]))
是否有:members 关键字或其他声明成员变量的方式?
【问题讨论】:
标签: clojure
如果提供,将创建一个具有给定名称的公共最终实例字段。您必须提供 :init 函数才能为状态提供值。请注意,虽然是最终状态,但状态可以是 ref 或代理,支持创建具有事务或异步突变语义的 Java 对象。
website 上有一个如何使用它的示例。
【讨论】:
我也遇到了一些麻烦。下面的例子并不优雅,但在 Clojure 而不是 Java 中编写愚蠢的小胶水类非常简单。请注意,我为线程安全所做的只是确保字段更新是原子的——我没有做任何其他并发性的事情,这可能会产生真正的影响。
init 方法为对象创建实例变量。 setfield 和 getfield 宏是原子更新记账的缩写。
(ns #^{:doc "A simple class with instance vars"
:author "David G. Durand"}
com.tizra.example )
(gen-class
:name com.tizra.example.Demo
:state state
:init init
:prefix "-"
:main false
:methods [[setLocation [String] void]
[getLocation [] String]]
)
(defn -init []
"store our fields as a hash"
[[] (atom {:location "default"})])
(defmacro setfield
[this key value]
`(swap! (.state ~this) into {~key ~value}))
(defmacro getfield
[this key]
`(@(.state ~this) ~key))
(defn -setLocation [this ^java.lang.String loc]
(setfield this :location loc))
(defn ^String -getLocation
[this]
(getfield this :location))
您必须对其进行编译,并确保生成的存根类在您的类路径中,然后您可以像任何其他 java 类一样创建实例等。
=> (com.tizra.example.Demo.)
#<Demo com.tizra.example.Demo@673a95af>
=> (def ex (com.tizra.example.Demo.))
#'user/ex
=> (.getLocation ex)
"default"
=> (.setLocation ex "time")
nil
=> (.getLocation ex)
"time"
我发现此博客上的较长摘要很有帮助:http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html
【讨论】:
proxy 的主体是一个词法闭包,因此您可以围绕您需要的任何变量进行闭包。如果,上帝保佑,你需要改变它们,然后关闭一个原子:
(defn lying-list [init-size]
(let [the-size (atom init-size)]
(proxy [java.util.ArrayList] []
(size [] @the-size)
(remove [idx] (reset! the-size idx), true))))
这里真的不需要实际的 Java 字段。
【讨论】: