Declare member variables with gen class in Clojure

I am learning how to extend Java classes in Clojure, but I see no way to declare new member variables; I only see a way for methods.

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

Is there a keyword :members or another way to declare member variables?

+4
source share
3 answers

: state name

If specified, a final destination public field with the given name will be created. You must provide a function: init to provide a value for the state. Note that although the final state may be a ref or an agent supporting the creation of Java objects with the semantics of a transactional or asynchronous mutation.

The website provides an example of how to use it.

+5
source

I also had problems with this. The example below is not elegant, but pretty simple for writing dumb little glue classes in Clojure, not in Java. Please note that all I did to ensure thread safety was to ensure that field updates were atomic - I did not use any other concurrency materials, and this can make a real difference.

The init method creates instance variables for the object. The setfield and getfield reduce atomic update accounting.

 (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 must compile this and make sure that the stub class is produced in your classpath, and then you can instantiate, etc. 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 a more detailed summary on this blog quite useful: http://kotka.de/blog/2010/02/gen-class_how_it_works_and_how_to_use_it.html

+1
source

The proxy body is a lexical closure, so you can just close any variables you need. If, God forbid, you need to mutate them, and then close them around the 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)))) 

It really does not need real Java fields.

+1
source

Source: https://habr.com/ru/post/986377/


All Articles