2009-10-18 62 views
4

我正在學習如何在Clojure中擴展Java類,但我沒有看到一種方式聲明新的成員變量;我只看到一種方法。用Clojure中的gen-class聲明成員變量

(ns test.aclass 
    (:gen-class 
    :methods [[foo [] String]])) 

是否有:members關鍵字或另一種聲明成員變量的方法?

回答

5

:state name

如果提供,具有給定名稱的公共終審場將被建立。您必須提供:init函數才能爲狀態提供值。請注意,雖然最終,狀態可以是參考或代理,從而支持使用事務性或異步變體語義創建Java對象。

website上有一個關於如何使用它的例子。

1

我也遇到了一些麻煩。下面的例子並不優雅,但在Clojure而不是Java中編寫愚蠢的小型粘連類非常簡單。注意我爲線程安全所做的一切是確保字段更新是原子的 - 我沒有做任何其他併發的東西,這可能會產生真正的變化。

init方法爲該對象創建實例變量。 setfieldgetfield宏縮寫原子更新的簿記。

(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)) 

您必須編譯這一點,並確保所產生的stub類是在classpath中,然後就可以使實例等,就像任何其他的Java類。

=> (com.tizra.example.Demo.) 
#<Demo [email protected]> 

=> (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

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領域。