Declare Member Variables with gen-class in Clojure

前端 未结 3 575
盖世英雄少女心
盖世英雄少女心 2021-01-13 20:49

I\'m learning how to extend Java classes in Clojure, but I don\'t see a way declare new member variables; I only see a way for methods.

(ns test.aclass
  (:g         


        
相关标签:
3条回答
  • 2021-01-13 20:54

    The body of a proxy is a lexical closure, so you can just close around whatever variables you need. If, God forbid, you need to mutate them, then close around an atom:

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

    There's really no need for actual Java fields here.

    0 讨论(0)
  • 2021-01-13 21:15

    I was having some trouble with this too. The example below is not elegant but it's pretty simple for writing dumb little glue classes in Clojure rather than Java. Note all I did for thread safety is to ensure that the field updates are atomic -- I didn't do any other concurrency stuff, and that may make a real difference.

    The init method creates the instance variables for the object. The setfield and getfield macros abbreviate the bookkeeping of the atomic update.

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

    You have to compile this, and make sure the stub class produced is on your classpath, and then you can make instances, etc. just like any other java class.

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

    I found the longer summary at this blog quite helpful: http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html

    0 讨论(0)
  • 2021-01-13 21:17

    :state name

    If supplied, a public final instance field with the given name will be created. You must supply an :init function in order to provide a value for the state. Note that, though final, the state can be a ref or agent, supporting the creation of Java objects with transactional or asynchronous mutation semantics.

    There is an example on the website of how it can be used.

    0 讨论(0)
提交回复
热议问题