我正在学习如何在Clojure中扩展Java类,但我没有看到声明新成员变量的方法;我只看到了一种方法。
(ns test.aclass
(:gen-class
:methods [[foo [] String]]))
是否有:members
关键字或其他声明成员变量的方式?
答案 0 :(得分:5)
如果提供,将创建具有给定名称的公共最终实例字段。您必须提供:init函数才能为状态提供值。请注意,尽管是final,但状态可以是ref或agent,支持使用事务或异步变异语义创建Java对象。
website上有一个如何使用它的例子。
答案 1 :(得分:1)
我也遇到了一些麻烦。下面的例子并不优雅,但是在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
答案 2 :(得分:1)
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字段。